<?php

namespace App\Http\Controllers\Agent;

use Agence104\LiveKit\AccessToken;
use Agence104\LiveKit\AccessTokenOptions;
use Agence104\LiveKit\EgressServiceClient;
use Agence104\LiveKit\RoomCreateOptions;
use Agence104\LiveKit\RoomServiceClient;
use Agence104\LiveKit\VideoGrant;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Classes\JassJWT;
use App\Events\AppointmentCallEnded;
use App\Events\AppointmentCreated;
use App\Events\DeviceCurrentStatus;
use App\Models\Appointment;
use App\Models\Doctor;
use App\Models\DoctorAppointment;
use App\Models\Lab;
use App\Models\Patient;
use App\Models\PatientNote;
use App\Models\Telemedpro;
use Carbon\Carbon;
use DateTime;
use Error;
use Exception;
use Illuminate\Support\Facades\Auth;
use Livekit\EncodedFileOutput;
use Livekit\EncodedFileType;
use Illuminate\Support\Facades\Http;

class MeetingController extends Controller
{
    public function getAsseblyAiToekn()
    {
        $response = Http::withHeaders([
            'Authorization' => '0014470a9fa14b598a73cc0133acca8c',
        ])->post('https://api.assemblyai.com/v2/realtime/token', [
            'expires_in' => 1600
        ]);

        return $response->body();
    }
    public function show($meeting_id)
    {
        return view('agent.jetsi', compact('meeting_id'));
    }
    public function joinMeeting($meeting_id)
    {
        $jassToken =  JassJWT::generate();
        $appoinement = Appointment::where("meeting_id", $meeting_id)->firstOrFail();

        return view('agent.join-meeting', compact('meeting_id'), compact('jassToken'));
    }
    public function startCall($patient_id, $agent_id, $appointment_id, Request $request)
    {
        $call_type = $request->input('call_type');
        $telemed_pro = Telemedpro::where("id", $agent_id)->firstOrFail();
        $appointment = Appointment::find($appointment_id);
        $appointment->in_call = 1;
        $appointment->start_time = Carbon::now()->format('Y-m-d H:i:s');
        $appointment_booking_tokens = $this->bookAppointmentApi($appointment, $telemed_pro);
        $appointment->agent_call_token = $appointment_booking_tokens['tokenAgent'];
        $appointment->patient_call_token = $appointment_booking_tokens['tokenPatient'];
        $appointment->telemed_pros_id = $agent_id;
        $appointment->save();

        event(new AppointmentCreated($patient_id, $appointment->id, $appointment->patient_call_token, $call_type));

        return response()->json([
            'message' => 'Appointment created!',
            'appointment_id' =>  $appointment->id,
            'meeting_id' =>  $appointment->agent_call_token,
            'call_type' => $call_type
        ], 200);
    }


    public function startRecording(Appointment $appointment)
    {
        $telemed = Telemedpro::find($appointment->telemed_pros_id);
        if ($telemed->recording_switch == 0) {
            return response()->json([
                'message' => "Recording Off",
                'Response' =>  'Error',
            ], 200);
        }
        $roomName = 'appointment-' . $appointment->id;
        $video_token = md5(rand(100000000, 999999999) . rand(1, 9));
        $appointment->video_token = $video_token;
        $appointment->save();

        $client = new EgressServiceClient("https://plugnmeet.codelfi.com", config('app.LK_API_KEY'), config('app.LK_API_SECRET'));
        try {
            $info = $client->startRoomCompositeEgress($roomName, 'speaker', new EncodedFileOutput([
                "filepath" => "/out/recordings/" . $video_token . ".mp4",
                "file_type" =>  EncodedFileType::MP4,
            ]));
        } catch (Exception $e) {
            return response()->json([
                'message' => $e->getMessage(),
                'Response' =>  'Error',
            ], 200);
        }
        return response()->json([
            'message' => "Success",
            'Response' =>  'Success',
        ], 200);
    }
    public function bookAppointmentApi($appointment, $availableTelemedPros)
    {
        $roomName = 'appointment-' . $appointment->id . "-" . uniqid();
        $opts = (new RoomCreateOptions())
            ->setName($roomName)
            ->setEmptyTimeout(30)
            ->setMaxParticipants(5);
        $host = "https://plugnmeet.codelfi.com";
        $svc = new RoomServiceClient($host, config('app.LK_API_KEY'), config('app.LK_API_SECRET'));
        try {
            $svc->deleteRoom($roomName);
        } catch (Exception | Error $e) {
        }

        $room = $svc->createRoom($opts);

        $participantPatientName = "patient-" . uniqid() . $appointment->patient->first_name . " " . $appointment->patient->last_name;

        $tokenOptionsPatient = (new AccessTokenOptions())
            ->setIdentity($participantPatientName);
        $videoGrantPatient = (new VideoGrant())
            ->setRoomJoin()
            ->setRoomName($roomName);
        $tokenPatient = (new AccessToken(config('app.LK_API_KEY'), config('app.LK_API_SECRET')))
            ->init($tokenOptionsPatient)
            ->setGrant($videoGrantPatient)
            ->toJwt();

        $participantAgentName = "agent-" . uniqid() . $availableTelemedPros->name;
        $tokenOptionsAgent = (new AccessTokenOptions())
            ->setIdentity($participantAgentName);
        $videoGrantAgent = (new VideoGrant())
            ->setRoomJoin()
            ->setRoomName($roomName);
        $tokenAgent = (new AccessToken(config('app.LK_API_KEY'), config('app.LK_API_SECRET')))
            ->init($tokenOptionsAgent)
            ->setGrant($videoGrantAgent)
            ->toJwt();
        return [
            'tokenPatient' => $tokenPatient,
            'tokenAgent' => $tokenAgent,
        ];
    }
    public function endCall($patient_id, $appointment_id)
    {
        $appointment = Appointment::find($appointment_id);
        $appointment->in_call = 0;
        $appointment->end_time =  Carbon::now()->format('Y-m-d H:i:s');
        $appointment->save();

        event(new AppointmentCallEnded($patient_id, $appointment->id));

        return response()->json([
            'message' => 'Call ended',
            'appointment_id' =>  $appointment->id,
        ], 200);
    }

    public function searchLabsByAddress(Request $request)
    {
        $address = $request->input('address');

        try {
            $labs = Lab::where('address', 'like', '%' . $address . '%')
                ->orWhere('city', 'like', '%' . $address . '%')
                ->orWhere('state', 'like', '%' . $address . '%')
                ->orWhere('zip_code', 'like', '%' . $address . '%')
                ->get(['id', 'name', 'city', 'state', 'zip_code', 'lang', 'lat']);

            return response()->json($labs, 200);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Failed to search labs'], 500);
        }
    }
    public function bookAppointment(Request $request)
    {
        $validatedData = $request->validate([
            'telemed_pros_id' => 'required|exists:telemed_pros,id',
            'patient_id' => 'required|exists:patients,id',
            'doctor_id' => 'required|exists:doctors,id',
            'appointment_id' => 'required',
            'appointment_time' => 'required|date_format:Y-m-d H:i:s',
        ]);

        $appointment = DoctorAppointment::create($validatedData);




        return response()->json([
            'message' => 'Appointment booked successfully',
            'meeting_id' => $appointment->meeting_id,
            'appointment_time' => $validatedData['appointment_time']
        ]);
    }
    public function updateInfo(Request $request, $patientId)
    {
        try {
            $patient = Patient::find($patientId);
            $patient->update($request->only(['city', 'state', 'address', 'zip_code', 'dob', 'country']));
            $patient->save();

            return response()->json(['message' => 'Patient address updated successfully'], 200);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }
    public function getInfo(Request $request, $patientId)
    {
        try {
            $patient = Patient::find($patientId)->makeHidden(['password', 'remember_token']);
            if ($patient->dob) {
                $birthDate = new DateTime($patient->dob);
                $today = new DateTime(date('Y-m-d'));
                $age = $today->diff($birthDate)->y;
                $patient->age = $age;
            } else {
                $patient->age = 0;
            }

            return response()->json($patient);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }
    public function getDoctorList()
    {
        try {
            // Fetch only the necessary columns for efficiency
            $doctors = Doctor::select('id', 'name', 'designation')->get()->makeHidden(['password', 'remember_token']);

            // Return a successful JSON response with the doctors
            return response()->json($doctors, 200);
        } catch (\Exception $e) {
            // Handle exceptions gracefully
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }
    public function getAppointmentList()
    {
        try {
            $appointments = Appointment::select("patients.first_name", "patients.last_name", "telemed_pros.name as agent_name", "appointments.*") // Eager load the associated telemed pro
                ->leftJoin("telemed_pros", "telemed_pros.id", "appointments.telemed_pros_id")
                ->leftJoin("patients", "patients.id", "appointments.patient_id")
                /* ->orderBy('appointment_time', 'desc') */ // Optional: sort by appointment time
                ->get();

            return response()->json($appointments, 200);
        } catch (\Exception $e) {

            return response()->json(['error' => 'Failed to retrieve appointments'], 500);
        }
    }
    public function getDoctorAppointmentList()
    {
        try {
            $appointments = DoctorAppointment::select("patients.first_name", "patients.last_name", "doctor_appointments.*") // Eager load the associated telemed pro
                ->leftJoin("patients", "patients.id", "doctor_appointments.patient_id")
                ->orderBy('doctor_appointments.created_at', 'desc') // Optional: sort by appointment time
                ->get();

            return response()->json($appointments, 200);
        } catch (\Exception $e) {

            return response()->json(['error' => 'Failed to retrieve appointments'], 500);
        }
    }
    public function availableSlots($date)
    {
        // Ensure date is in a valid format
        $date = Carbon::parse($date);

        // Generate all possible 30-minute slots between 9 AM and 4 PM
        $slots = collect();
        $startTime = Carbon::parse($date)->setTime(9, 0, 0);
        $endTime = Carbon::parse($date)->setTime(16, 0, 0);
        while ($startTime < $endTime) {
            $slots->push($startTime->format('Y-m-d H:i:s'));
            $startTime->addMinutes(30);
        }
        // Filter out booked slots
        $bookedAppointments = Appointment::where('appointment_date', '>=', $date->format('Y-m-d'))
            ->where('appointment_date', '<', $date->addDay()->format('Y-m-d'))
            ->pluck('appointment_date');
        $availableSlots = $slots->diff($bookedAppointments);

        $formattedSlots = $availableSlots->map(function ($slot) {

            $start = Carbon::parse($slot);

            // Add AM/PM
            $startTime = $start->format('g:i A');

            $end = (clone $start)->addMinutes(29);

            // Add AM/PM
            $endTime = $end->format('g:i A');

            return $startTime /* . ' - ' . $endTime */;
        });

        // Additional checking if slot is booked
        $formattedSlots = $formattedSlots->filter(function ($slot) {

            $startTime = Carbon::parse($slot);
            /*$startTime = Carbon::parse(explode(' - ', $slot)[0]);
            $endTime = Carbon::parse(explode(' - ', $slot)[1]); */

            //return !Appointment::whereBetween('appointment_time', [$startTime/* , $endTime */])->exists();
            return !Appointment::where('appointment_time', $startTime)->exists();
        });

        return response()->json([
            'available_slots' => $formattedSlots->toArray()
        ]);
    }
    public function appointmentDetail(Appointment $appointment)
    {
        $patient = Patient::find($appointment->patient_id);
        $telemedPro = Telemedpro::find($appointment->telemed_pros_id);
        $doctor_appointment = DoctorAppointment::select("id", "appointment_date", "appointment_time", "appointment_id")->where('appointment_id', $appointment->id)->first();
        if (!$doctor_appointment)
            $doctor_appointment = [];
        else
            $doctor_appointment = $doctor_appointment->toArray();

        if ($patient) {
            if ($patient->dob) {
                $birthDate = new DateTime($patient->dob);
                $today = new DateTime(date('Y-m-d'));
                $age = $today->diff($birthDate)->y;
                $patient->age = $age;
            } else {
                $patient->age = 0;
            }
        }

        return response()->json([
            'patient' => $patient->toArray() ?? [],
            'telemedPro' => $telemedPro->toArray() ?? [],
            'doctor_appointment' => $doctor_appointment ?? [],
            'agent_appointment' => $appointment,
            'video_url' => "https://plugnmeet.codelfi.com/recordings/" . $appointment->video_token . ".mp4",
        ]);
    }
    public function labDetail(Appointment $appointment)
    {
        $lab = Lab::where("appointment_id", $appointment->id)->first();
        return response()->json([
            'lab_name' => $lab->name,
            'lab_address' => $lab->address,
            'lab_city' => $lab->city,
            'lab_state' => $lab->state,
            'lab_distance' => $lab->distance,
            'lab_contact_no' => $lab->contact_no,
            'lab_lang' => $lab->lang,
            'lab_lat' => $lab->lat,
            'slot_date' => $lab->slot_date,
            'slot_time' => $lab->slot_time,
            'booking_time' => $lab->booking_time,
        ]);
    }
    public function getRoomList()
    {
        $svc = new RoomServiceClient("https://plugnmeet.codelfi.com", config('app.LK_API_KEY'), config('app.LK_API_SECRET'));

        // List rooms.
        $rooms = $svc->listRooms();

        dd($rooms);
    }

    public function addNotePatient(Request $request)
    {
        // Validation (adjust as needed)

        $patient = Auth::guard('patient')->user();

        $addNotePatient = PatientNote::create([
            'note' => $request->input('note'),
            'note_type' => $request->input('note_type'),
            'patient_id' => $patient->id,
        ]);

        return response()->json([
            'message' => 'Note created',
            'data' => $addNotePatient
        ], 200);
    }

    public function getNotePatient(Request $request)
    {
        // Validation (adjust as needed)

        $patient = Auth::guard('patient')->user();

        $addNotePatient = PatientNote::where("patient_id", $patient->id)->get();

        return response()->json([
            'message' => 'Note created',
            'data' => $addNotePatient
        ], 200);
    }
    public function markAppointmentsStatus($id)
    {
        $appointment = Appointment::find($id);
        $appointment->status = 'completed';
        $appointment->save();
        return response()->json([
            'message' => 'status updated !'
        ], 200);
    }
}
