<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;

class FaceRecognitionController extends Controller
{
    /**
     * Face register login API
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function faceRegisterLogin(Request $request)
    {

        
        $validator = Validator::make($request->all(), [
            'locker_number' => 'required|string|max:50'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Check locker_number in locker_registration_items table
            $lockerRegistrationItem = DB::table('locker_registration_items')
                ->where('locker_number', $request->locker_number)
                ->first();

            if (!$lockerRegistrationItem) {
                return response()->json([
                    'success' => false,
                    'message' => 'Locker number not found'
                ], 404);
            }

            // Get registration details from locker_registrations table
            $lockerRegistration = DB::table('locker_registrations')
                ->where('id', $lockerRegistrationItem->registration_id)
                ->first();

            if (!$lockerRegistration) {
                return response()->json([
                    'success' => false,
                    'message' => 'Registration not found'
                ], 404);
            }

            // Get client details from clients table
            $client = DB::table('clients')
                ->where('id', $lockerRegistration->client_id)
                ->first();

            if (!$client) {
                return response()->json([
                    'success' => false,
                    'message' => 'Client not found'
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => "Welcome {$client->name}",
                'data' => [
                    'client_id' => $client->id,
                    'client_name' => $client->name,
                    'locker_number' => $request->locker_number,
                    'registration_id' => $lockerRegistration->id
                ]
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Login failed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get customer members by client_id
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getCustomerMembers(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'client_id' => 'required|integer'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Fetch customer_members using client_id
            $customerMembers = DB::table('customer_members')
                ->where('client_id', $request->client_id)
                ->get();

            if ($customerMembers->isEmpty()) {
                return response()->json([
                    'success' => false,
                    'message' => 'No customer members found for this client'
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => 'Customer members retrieved successfully',
                'data' => [
                    'client_id' => $request->client_id,
                    'members' => $customerMembers,
                    'total_members' => $customerMembers->count()
                ]
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to retrieve customer members',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Store face descriptor for customer member
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function storeFace(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'customer_member_id' => 'required|integer',
            'face_descriptor' => 'required|array'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Check if customer member exists
            $customerMember = DB::table('customer_members')
                ->where('id', $request->customer_member_id)
                ->first();

            if (!$customerMember) {
                return response()->json([
                    'success' => false,
                    'message' => 'Customer member not found'
                ], 404);
            }

            // Convert face descriptor array to JSON string for storage
            $faceDescriptorJson = json_encode($request->face_descriptor);

            // Update customer member with face descriptor and verification status
            $updated = DB::table('customer_members')
                ->where('id', $request->customer_member_id)
                ->update([
                    'face_descriptor' => $faceDescriptorJson,
                    'is_face_verified' => 1,
                    'status' => 1,
                    'updated_at' => now()
                ]);

            if ($updated) {
                return response()->json([   
                    'success' => true,
                    'message' => 'Face descriptor stored successfully',
                    'data' => [
                        'customer_member_id' => $request->customer_member_id,
                        'face_descriptor_stored' => true,
                        'is_face_verified' => 1,
                        'status' => 1
                    ]
                ]);
            } else {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to update customer member'
                ], 500);
            }

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to store face descriptor',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Door app login API with appointment validation
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function doorAppLogin(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'locker_number' => 'required|string|max:50'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Check locker_number in locker_registration_items table
            $lockerRegistrationItem = DB::table('locker_registration_items')
                ->where('locker_number', $request->locker_number)
                ->first();

            if (!$lockerRegistrationItem) {
                return response()->json([
                    'success' => false,
                    'message' => 'Locker number not found'
                ], 404);
            }

            // Get registration details from locker_registrations table
            $lockerRegistration = DB::table('locker_registrations')
                ->where('id', $lockerRegistrationItem->registration_id)
                ->first();

            if (!$lockerRegistration) {
                return response()->json([
                    'success' => false,
                    'message' => 'Registration not found'
                ], 404);
            }

            // Get client details from clients table
            $client = DB::table('clients')
                ->where('id', $lockerRegistration->client_id)
                ->first();

            if (!$client) {
                return response()->json([
                    'success' => false,
                    'message' => 'Client not found'
                ], 404);
            }

            // Check if client has appointment for today
            $today = now()->format('Y-m-d');
            $appointment = DB::table('appointments')
                ->where('client_id', $client->id)
                ->whereDate('appointment_date', $today)
                ->first();

            if (!$appointment) {
                return response()->json([
                    'success' => false,
                    'message' => 'No appointment found for today. Please create an appointment or contact admin'
                ], 403);
            }

            // Handle different appointment statuses
            switch ($appointment->status) {
                case 'confirmed':
                    // Allow login for confirmed appointments - HTTP 200
                    return response()->json([
                        'success' => true,
                        'message' => "Welcome {$client->name}",
                        'data' => [
                            'client_id' => $client->id,
                            'client_name' => $client->name,
                            'locker_number' => $request->locker_number,
                            'registration_id' => $lockerRegistration->id,
                            'appointment_id' => $appointment->id,
                            'appointment_date' => $appointment->appointment_date,
                            'appointment_status' => $appointment->status
                        ]
                    ], 200);

                case 'pending':
                    // Pending appointment - HTTP 202 Accepted
                    return response()->json([
                        'success' => false,
                        'message' => 'Your appointment is pending confirmation',
                        'data' => [
                            'client_id' => $client->id,
                            'client_name' => $client->name,
                            'appointment_id' => $appointment->id,
                            'appointment_date' => $appointment->appointment_date,
                            'appointment_status' => $appointment->status
                        ]
                    ], 202);

                case 'completed':
                    // Appointment already completed - HTTP 410 Gone
                    return response()->json([
                        'success' => false,
                        'message' => 'Your appointment has already been completed',
                        'data' => [
                            'client_id' => $client->id,
                            'client_name' => $client->name,
                            'appointment_id' => $appointment->id,
                            'appointment_date' => $appointment->appointment_date,
                            'appointment_status' => $appointment->status
                        ]
                    ], 410);

                case 'cancelled':
                    // Appointment was cancelled - HTTP 423 Locked
                    return response()->json([
                        'success' => false,
                        'message' => 'Your appointment has been cancelled',
                        'data' => [
                            'client_id' => $client->id,
                            'client_name' => $client->name,
                            'appointment_id' => $appointment->id,
                            'appointment_date' => $appointment->appointment_date,
                            'appointment_status' => $appointment->status
                        ]
                    ], 423);

                default:
                    // Unknown status - HTTP 422 Unprocessable Entity
                    return response()->json([
                        'success' => false,
                        'message' => 'Appointment status is unknown. Please contact admin',
                        'data' => [
                            'appointment_status' => $appointment->status
                        ]
                    ], 422);
            }

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Login failed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get today's appointments by client_id
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getTodayAppointments(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'client_id' => 'required|integer|exists:clients,id'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Get today's date
            $today = now()->format('Y-m-d');

            // Fetch today's appointments for the client
            $appointments = DB::table('appointments')
                ->where('client_id', $request->client_id)
                ->whereDate('appointment_date', $today)
                ->get();

            if ($appointments->isEmpty()) {
                return response()->json([
                    'success' => false,
                    'message' => 'No appointments found for today'
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => 'Today\'s appointments retrieved successfully',
                'data' => [
                    'client_id' => $request->client_id,
                    'date' => $today,
                    'appointments' => $appointments,
                    'total_appointments' => $appointments->count()
                ]
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to retrieve appointments',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get verified attendees by appointment_id
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getVerifiedAttendees(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'appointment_id' => 'required|integer|exists:appointments,id'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        try {
            // Get appointment details including attendees
            $appointment = DB::table('appointments')
                ->where('id', $request->appointment_id)
                ->first();

            if (!$appointment) {
                return response()->json([
                    'success' => false,
                    'message' => 'Appointment not found'
                ], 404);
            }

            // Decode attendees JSON array
            $attendeeIds = json_decode($appointment->attendees, true);

            if (!$attendeeIds || !is_array($attendeeIds) || empty($attendeeIds)) {
                return response()->json([
                    'success' => false,
                    'message' => 'No attendees found for this appointment'
                ], 404);
            }

            // Fetch customer members with face verification
            $verifiedAttendees = DB::table('customer_members')
                ->whereIn('id', $attendeeIds)
                ->where('is_face_verified', 1)
                ->where('status', 1)
                ->get(['id', 'name', 'email', 'mobile', 'is_face_verified', 'status']);

            if ($verifiedAttendees->isEmpty()) {
                return response()->json([
                    'success' => false,
                    'message' => 'No verified attendees found'
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => 'Verified attendees retrieved successfully',
                'data' => [
                    'appointment_id' => $request->appointment_id,
                    'total_attendees' => count($attendeeIds),
                    'verified_attendees' => $verifiedAttendees,
                    'verified_count' => $verifiedAttendees->count()
                ]
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to retrieve attendees',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Face verification API
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function faceverify(Request $request)
    {
        $requestData = $request->json()->all();
        
        $validator = Validator::make($requestData, [
            'customer_member_id' => 'required|integer|exists:customer_members,id',
            'face_descriptor' => 'required|array',
            'face_descriptor.*' => 'required|numeric|between:-1,1'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

        $inputFaceDescriptor = $requestData['face_descriptor'];
        $customerMemberId = $requestData['customer_member_id'];
        
        try {
            // Get customer member by ID with face descriptor
            $customerMember = DB::table('customer_members')
                ->where('id', $customerMemberId)
                ->whereNotNull('face_descriptor')
                ->first();
            
            if (!$customerMember) {
                return response()->json([
                    'success' => false,
                    'message' => 'Customer member not found or no face data registered'
                ], 404);
            }

            $storedFaceDescriptors = json_decode($customerMember->face_descriptor, true);

            if (!$storedFaceDescriptors || !is_array($storedFaceDescriptors)) {
                return response()->json([
                    'success' => false,
                    'message' => 'Invalid face descriptor data stored'
                ], 400);
            }

            // Normalize stored descriptors to multi-dimensional array
            $descriptorsToCheck = [];
            if (isset($storedFaceDescriptors[0]) && is_array($storedFaceDescriptors[0])) {
                // Already multiple descriptors stored
                $descriptorsToCheck = $storedFaceDescriptors;
            } else {
                // Single descriptor stored, convert to multi-dimensional
                $descriptorsToCheck = [$storedFaceDescriptors];
            }

            $bestSimilarity = 0;

            // Compare with all stored descriptors
            foreach ($descriptorsToCheck as $index => $storedDescriptor) {
                if (!is_array($storedDescriptor)) {
                    continue;
                }

                $similarity = $this->cosineSimilarity($inputFaceDescriptor, $storedDescriptor);

                // Keep track of best match
                if ($similarity > $bestSimilarity) {
                    $bestSimilarity = $similarity;
                }
            }

            // Check if any descriptor matched
            $matched = $bestSimilarity >= 0.65;

            if ($matched) {
                // Smart Learning: Check if new descriptor should be saved
                $shouldSaveNew = true;
                
                foreach ($descriptorsToCheck as $existingDescriptor) {
                    $similarity = $this->cosineSimilarity($inputFaceDescriptor, $existingDescriptor);
                    if ($similarity >= 0.85) {
                        $shouldSaveNew = false; // Already similar enough, don't save duplicate
                        break;
                    }
                }

                // Add new descriptor if it's different enough
                if ($shouldSaveNew) {
                    $descriptorsToCheck[] = $inputFaceDescriptor;
                }

                // Limit storage to latest 10 descriptors
                if (count($descriptorsToCheck) > 10) {
                    array_shift($descriptorsToCheck); // Remove oldest
                }

                // Update database if changes were made
                if ($shouldSaveNew || count($descriptorsToCheck) > 10) {
                    DB::table('customer_members')
                        ->where('id', $customerMemberId)
                        ->update([
                            'face_descriptor' => json_encode($descriptorsToCheck),
                            'updated_at' => now()
                        ]);
                }

                return response()->json([
                    'success' => true,
                    'message' => 'Face verification successful',
                    'data' => [
                        'customer_member_id' => $customerMember->id,
                        'name' => $customerMember->name,
                        'matched' => true,
                        'confidence' => round($bestSimilarity, 4),
                        'threshold' => 0.65,
                        'total_faces' => count($descriptorsToCheck)
                    ]
                ], 200);
            } else {
                return response()->json([
                    'success' => false,
                    'message' => 'Face verification failed',
                    'data' => [
                        'customer_member_id' => $customerMember->id,
                        'name' => $customerMember->name,
                        'matched' => false,
                        'confidence' => round($bestSimilarity, 4),
                        'threshold' => 0.65
                    ]
                ], 200);
            }

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Face verification failed',
                'error' => $e->getMessage()
            ], 500);
        }
    }
    
    /**
     * Calculate cosine similarity between two vectors
     */
    private function cosineSimilarity(array $vectorA, array $vectorB): float
    {
        $minLength = min(count($vectorA), count($vectorB));
        
        if ($minLength == 0) {
            return 0;
        }

        $dotProduct = 0;
        $magnitudeA = 0;
        $magnitudeB = 0;

        for ($i = 0; $i < $minLength; $i++) {
            $dotProduct += $vectorA[$i] * $vectorB[$i];
            $magnitudeA += $vectorA[$i] * $vectorA[$i];
            $magnitudeB += $vectorB[$i] * $vectorB[$i];
        }

        $magnitudeA = sqrt($magnitudeA);
        $magnitudeB = sqrt($magnitudeB);

        if ($magnitudeA == 0 || $magnitudeB == 0) {
            return 0;
        }

        return $dotProduct / ($magnitudeA * $magnitudeB);
    }
}
