<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\CreateVisitRequest;
use App\Http\Requests\UpdateVisitRequest;
use App\Models\Visit;
use App\Models\UserRole;
use App\Models\Location;
use App\Services\RequestIdService;
use App\Services\TokenService;
use App\Services\EmailService;
use App\Services\VisitStatusService;
use Illuminate\Http\Request;

class VisitController extends Controller
{
    public function __construct(
        private RequestIdService $requestIdService,
        private TokenService $tokenService,
        private EmailService $emailService,
        private VisitStatusService $statusService
    ) {}

    /**
     * List all visits with filtering and pagination
     */
    public function index(Request $request)
    {
        $query = Visit::with(['location', 'mainVisitor', 'host']);

        // Filter by status
        if ($request->has('status')) {
            $query->where('status', $request->status);
        }

        // Filter by location
        if ($request->has('location_id')) {
            $query->where('location_id', $request->location_id);
        }

        // Filter by date range (handle each separately)
        if ($request->has('date_from')) {
            $query->where('date', '>=', $request->date_from);
        }
        if ($request->has('date_to')) {
            $query->where('date', '<=', $request->date_to);
        }

        // Search - search in request_id, host_name, main_visitor_email, and main visitor's name/email
        if ($request->has('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('request_id', 'like', "%{$search}%")
                    ->orWhere('host_name', 'like', "%{$search}%")
                    ->orWhere('main_visitor_email', 'like', "%{$search}%")
                    ->orWhereHas('mainVisitor', function ($q) use ($search) {
                        $q->where('name', 'like', "%{$search}%")
                          ->orWhere('email', 'like', "%{$search}%");
                    });
            });
        }

        // Filter by host (for regular users)
        $user = $request->user();

        if ($user && !$user->hasRole(['admin', 'security_member', 'security_admin'])) {
            $query->where('host_email', $user->email);
        }

        // Sorting
        $sortBy = $request->get('sort_by', 'created_at');
        $sortOrder = $request->get('sort_order', 'desc');
        $query->orderBy($sortBy, $sortOrder);

        // Pagination
        $perPage = $request->get('per_page', 25);
        $visits = $query->paginate($perPage);

        // Transform response
        $transformedVisits = $visits->through(function ($visit) {
            return $this->transformVisitForList($visit);
        });

        return response()->json($transformedVisits);
    }

    /**
     * Get single visit details
     */
    public function show(Request $request, int $id)
    {
        $visit = Visit::with([
            'location',
            'mainVisitor',
            'additionalVisitors'
        ])->findOrFail($id);

        // Authorization check
        $user = $request->user();

        if (!$user->hasRole(['admin', 'security_member', 'security_admin']) &&
            $visit->host_email !== $user->email) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        $transformedVisit = $this->transformVisitForDetail($visit);

        return response()->json($transformedVisit);
    }

    /**
     * Create new visit
     */
    public function store(CreateVisitRequest $request)
    {
        $hostVisitor = $request->hostVisitor;
        $location = Location::findOrFail($hostVisitor['location']);

        // Build visit data
        $data = [
            'request_id' => $this->requestIdService->generate(),
            'host_email' => $hostVisitor['email'],
            'host_name' => $hostVisitor['name'],
            'host_phone' => $hostVisitor['phone'],
            'location_id' => $hostVisitor['location'],
            'visit_reason' => $request->visitReason,
            'main_visitor_email' => $request->mainVisitorEmail,
            'meeting_type' => $request->meetingType,
            'status' => 'Pending Visitor', // Explicitly set initial status
            'registration_token' => $this->tokenService->generateRegistrationToken(),
            'token_expires_at' => $this->tokenService->getTokenExpiration(),
            'number_of_visitors' => 1, // Will be updated when visitors register
        ];

        if ($request->meetingType === 'single') {
            $data['date'] = $request->date;
            $data['start_time'] = $this->parseTimeString($request->startTime);
            $data['end_time'] = $this->parseTimeString($request->endTime);
        } else {
            $data['date_from'] = $request->dateFrom;
            $data['date_to'] = $request->dateTo;
            // Use default times for multiple day meetings
            $data['start_time'] = $this->parseTimeString($request->startTime ?? '08:00 AM');
            $data['end_time'] = $this->parseTimeString($request->endTime ?? '06:00 PM');
        }

        $visit = Visit::create($data);

        // Log visit creation
        \App\Models\VisitStatusHistory::create([
            'visit_id' => $visit->id,
            'from_status' => null,
            'to_status' => $visit->status,
            'changed_by_email' => $visit->host_email,
            'changed_by_name' => $visit->host_name,
            'reason' => 'Visit created',
        ]);

        // Send emails
        $this->emailService->sendVisitCreatedEmails($visit->fresh('location'));

        return response()->json([
            'id' => $visit->id,
            'requestId' => $visit->request_id,
            'status' => $visit->status,
            'registrationToken' => $visit->registration_token,
            'registrationUrl' => config('app.frontend_url') . '/visitor/register/' . $visit->registration_token,
            'message' => 'Visit created successfully. Invitation sent to visitor.',
        ], 201);
    }

    /**
     * Update visit
     */
    public function update(UpdateVisitRequest $request, int $id)
    {
        $visit = Visit::findOrFail($id);

        // Authorization check
        $user = $request->user();

        if (!$user->hasRole(['admin']) && $visit->host_email !== $user->email) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Check if visit can be edited
        if (!$visit->canBeEdited()) {
            return response()->json([
                'message' => 'Cannot edit this visit in current status',
            ], 403);
        }

        // Store old status for history logging
        $oldStatus = $visit->status;

        // Update fields
        if ($request->has('hostLocation')) {
            $visit->location_id = $request->hostLocation;
        }

        if ($request->has('visitReason')) {
            $visit->visit_reason = $request->visitReason;
        }

        // Cannot change visitor email if already registered
        if ($request->has('mainVisitorEmail') && !$visit->visitor_registered_at) {
            $visit->main_visitor_email = $request->mainVisitorEmail;
        }

        if ($request->has('meetingType')) {
            $visit->meeting_type = $request->meetingType;
        }

        if ($request->has('date')) {
            $visit->date = $request->date;
        }

        if ($request->has('startTimeHour') && $request->has('startTimeMinute') && $request->has('startTimeAmPm')) {
            $visit->start_time = $this->formatTime(
                $request->startTimeHour,
                $request->startTimeMinute,
                $request->startTimeAmPm
            );
        }

        if ($request->has('endTimeHour') && $request->has('endTimeMinute') && $request->has('endTimeAmPm')) {
            $visit->end_time = $this->formatTime(
                $request->endTimeHour,
                $request->endTimeMinute,
                $request->endTimeAmPm
            );
        }

        if ($request->has('dateFrom')) {
            $visit->date_from = $request->dateFrom;
        }

        if ($request->has('dateTo')) {
            $visit->date_to = $request->dateTo;
        }

        // If status was "Pending Host" (returned), change it to "Pending Visitor" after host corrections
        if ($oldStatus === 'Pending Host') {
            $visit->status = 'Pending Visitor';
            $visit->rejection_reason = null; // Clear the return reason
        }

        $visit->save();

        // Log visit update
        \App\Models\VisitStatusHistory::create([
            'visit_id' => $visit->id,
            'from_status' => $oldStatus,
            'to_status' => $visit->status,
            'changed_by_email' => $user->email,
            'changed_by_name' => $user->role,
            'reason' => $oldStatus === 'Pending Host' ? 'Visit corrected and resubmitted by host' : 'Visit details updated',
        ]);

        // If status changed from "Pending Host" to "Pending Visitor", resend invitation to visitor
        if ($oldStatus === 'Pending Host' && $visit->status === 'Pending Visitor') {
            $this->emailService->sendVisitCreatedEmails($visit->fresh('location'));
        }

        return response()->json([
            'id' => $visit->id,
            'requestId' => $visit->request_id,
            'message' => 'Visit updated successfully',
        ]);
    }

    /**
     * Delete/cancel visit
     */
    public function destroy(Request $request, int $id)
    {
        $visit = Visit::findOrFail($id);

        // Get user info
        $user = $request->user();

        // Authorization check - only admin or the host can delete
        if (!$user->hasRole(['admin']) && $visit->host_email !== $user->email) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Check if visit can be deleted (not Completed, Rejected, Expired, or Canceled)
        if (!$visit->canBeDeleted()) {
            return response()->json([
                'message' => 'Cannot cancel visit. Only visits with status Pending Visitor, Pending Approve, Pending Host, or Approved can be canceled.',
                'current_status' => $visit->status,
            ], 422);
        }

        // Soft delete by setting status to Canceled
        $this->statusService->transition(
            $visit,
            'Canceled',
            'Visit canceled by ' . ($user->hasRole(['admin']) ? 'admin' : 'host'),
            $user->email,
            $user->name ?? null
        );

        return response()->json([
            'message' => 'Visit canceled successfully',
            'status' => 'Canceled',
        ]);
    }

    /**
     * Get tracking history for a visit
     */
    public function trackingHistory(Request $request, int $id)
    {
        $visit = Visit::findOrFail($id);

        // Get user info
        $user = $request->user();

        // Authorization check - admin, security, or the host can view history
        if (!$user->hasRole(['admin', 'security_member', 'security_admin']) && $visit->host_email !== $user->email) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Get status history with changed by user info
        $history = $visit->statusHistory()->orderBy('created_at', 'asc')->get()->map(function ($entry) {
            return [
                'id' => $entry->id,
                'from_status' => $entry->from_status,
                'to_status' => $entry->to_status,
                'changed_by_email' => $entry->changed_by_email,
                'changed_by_name' => $entry->changed_by_name,
                'reason' => $entry->reason,
                'changed_at' => $entry->created_at->format('Y-m-d H:i:s'),
                'changed_at_human' => $entry->created_at->diffForHumans(),
            ];
        });

        return response()->json([
            'visit_id' => $visit->id,
            'request_id' => $visit->request_id,
            'current_status' => $visit->status,
            'history' => $history,
        ]);
    }

    /**
     * Approve visit (Security role)
     */
    public function approve(Request $request, int $id)
    {
        $visit = Visit::with(['mainVisitor', 'location'])->findOrFail($id);

        $user = $request->user();

        // Check authorization
        if (!$user->hasRole(['security_member', 'security_admin', 'admin'])) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Transition to Approved
        $this->statusService->transition(
            $visit,
            'Approved',
            null,
            $user->email,
            $user->role ?? null
        );

        // Send approval emails
        $this->emailService->sendApprovalEmails($visit);

        return response()->json([
            'message' => 'Visit approved successfully',
            'status' => 'Approved',
        ]);
    }

    /**
     * Reject visit
     */
    public function reject(Request $request, int $id)
    {
        $request->validate([
            'reason' => 'required|string|min:10|max:500',
        ]);

        $visit = Visit::findOrFail($id);

        $user = $request->user();

        // Check authorization
        if (!$user->hasRole(['security_member', 'security_admin', 'admin'])) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Transition to Rejected
        $this->statusService->transition(
            $visit,
            'Rejected',
            $request->reason,
            $user->email,
            $user->role ?? null
        );

        // Send rejection email
        $this->emailService->sendRejectionEmail($visit);

        return response()->json([
            'message' => 'Visit rejected',
            'status' => 'Rejected',
        ]);
    }

    /**
     * Return visit to host for corrections
     */
    public function return(Request $request, int $id)
    {
        $request->validate([
            'reason' => 'required|string|min:10|max:500',
        ]);

        $visit = Visit::findOrFail($id);

        $user = $request->user();

        // Check authorization
        if (!$user->hasRole(['security_member', 'security_admin', 'admin'])) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Transition to Pending Host
        $this->statusService->transition(
            $visit,
            'Pending Host',
            $request->reason,
            $user->email,
            $user->role ?? null
        );

        // Send return email
        $this->emailService->sendSecurityReturnEmail($visit);

        return response()->json([
            'message' => 'Visit returned to host for corrections',
            'status' => 'Pending Host',
            'data' => [
                'id' => $visit->id,
                'status' => $visit->status,
                'rejection_reason' => $visit->rejection_reason,
            ],
        ]);
    }

    /**
     * Send reminder email to visitor
     */
    public function sendReminder(Request $request, int $id)
    {
        $visit = Visit::findOrFail($id);

        $user = $request->user();

        // Only host or admin can send reminder
        if (!$user->hasRole(['admin']) && $visit->host_email !== $user->email) {
            return response()->json(['message' => 'Forbidden'], 403);
        }

        // Only send reminder if status is Pending Visitor
        if ($visit->status !== 'Pending Visitor') {
            return response()->json([
                'message' => 'Reminder can only be sent for pending visitor status',
            ], 400);
        }

        $this->emailService->sendReminderEmail($visit);

        // Log reminder sent
        \App\Models\VisitStatusHistory::create([
            'visit_id' => $visit->id,
            'from_status' => $visit->status,
            'to_status' => $visit->status,
            'changed_by_email' => $user->email,
            'changed_by_name' => $user->role,
            'reason' => 'Reminder email sent to visitor',
        ]);

        return response()->json([
            'message' => 'Reminder sent successfully',
        ]);
    }

    /**
     * Parse time string from "HH:MM AM/PM" format to 24-hour "HH:MM:SS" format
     */
    private function parseTimeString(string $timeString): string
    {
        // Match format like "09:00 AM" or "9:00 PM"
        if (preg_match('/^(0?[1-9]|1[0-2]):([0-5][0-9]) (AM|PM)$/', $timeString, $matches)) {
            $hour = (int)$matches[1];
            $minute = $matches[2];
            $amPm = $matches[3];

            // Convert to 24-hour format
            $hour24 = $amPm === 'AM'
                ? ($hour === 12 ? 0 : $hour)
                : ($hour === 12 ? 12 : $hour + 12);

            return sprintf('%02d:%s:00', $hour24, $minute);
        }

        // Fallback to default
        return '00:00:00';
    }

    /**
     * Transform visit for list response
     */
    private function transformVisitForList(Visit $visit): array
    {
        // Format dates based on meeting type
        if ($visit->meeting_type === 'single') {
            $startDate = $visit->date->format('d-m-Y') . ' ' .
                         \Carbon\Carbon::parse($visit->start_time)->format('H:i');
            $endDate = $visit->date->format('d-m-Y') . ' ' .
                       \Carbon\Carbon::parse($visit->end_time)->format('H:i');
        } else {
            $startDate = $visit->date_from->format('d-m-Y') . ' ' .
                         \Carbon\Carbon::parse($visit->start_time)->format('H:i');
            $endDate = $visit->date_to->format('d-m-Y') . ' ' .
                       \Carbon\Carbon::parse($visit->end_time)->format('H:i');
        }

        return [
            'id' => (string)$visit->id,
            'requestId' => $visit->request_id,
            'hostName' => $visit->host_name,
            'location' => $visit->location?->name ?? '',
            'mainVisitor' => $visit->mainVisitor?->name ?? $visit->main_visitor_email,
            'numberOfVisitors' => $visit->number_of_visitors,
            'status' => $visit->status,
            'startDate' => $startDate,
            'endDate' => $endDate,
        ];
    }

    /**
     * Transform visit for detail view
     */
    private function transformVisitForDetail(Visit $visit): array
    {
        // Transform main visitor
        $mainVisitor = [
            'id' => $visit->mainVisitor?->id ? (string)$visit->mainVisitor->id : null,
            'name' => $visit->mainVisitor?->name ?? null,
            'email' => $visit->main_visitor_email,
            'phone' => $visit->mainVisitor?->phone ?? null,
            'companyName' => $visit->mainVisitor?->company_name ?? null,
            'idFrontImage' => $visit->mainVisitor?->id_front_image_url ?? null,
            'idBackImage' => $visit->mainVisitor?->id_back_image_url ?? null,
        ];

        // Transform additional visitors
        $additionalVisitors = $visit->additionalVisitors->map(function ($visitor) {
            return [
                'id' => (string)$visitor->id,
                'name' => $visitor->name,
                'email' => $visitor->email,
                'phone' => $visitor->phone,
                'companyName' => $visitor->company_name ?? null,
                'idFrontImage' => $visitor->id_front_image_url ?? null,
                'idBackImage' => $visitor->id_back_image_url ?? null,
            ];
        })->toArray();

        // Convert times from 24-hour to 12-hour AM/PM format
        $startTime = $this->formatTimeTo12Hour($visit->start_time);
        $endTime = $this->formatTimeTo12Hour($visit->end_time);

        // Build response
        $response = [
            'id' => (string)$visit->id,
            'requestId' => $visit->request_id,
            'hostVisitor' => [
                'name' => $visit->host_name,
                'email' => $visit->host_email,
                'phone' => $visit->host_phone,
                'location' => $visit->location?->name ?? '',
            ],
            'mainVisitor' => $mainVisitor,
            'additionalVisitors' => $additionalVisitors,
            'visitReason' => $visit->visit_reason,
            'meetingType' => $visit->meeting_type,
            'startTime' => $startTime,
            'endTime' => $endTime,
            'status' => $visit->status,
            'numberOfVisitors' => $visit->number_of_visitors,
        ];

        // Add date fields based on meeting type
        if ($visit->meeting_type === 'single') {
            $response['date'] = $visit->date?->format('Y-m-d') ?? null;
        } else {
            $response['dateFrom'] = $visit->date_from?->format('Y-m-d') ?? null;
            $response['dateTo'] = $visit->date_to?->format('Y-m-d') ?? null;
        }

        // Add rejection reason if status is Rejected
        if ($visit->status === 'Rejected' && $visit->rejection_reason) {
            $response['rejectionReason'] = $visit->rejection_reason;
        }

        // Add return reason if status is Pending Host
        if ($visit->status === 'Pending Host' && $visit->rejection_reason) {
            $response['returnReason'] = $visit->rejection_reason;
        }

        return $response;
    }

    /**
     * Get publish link with token for sharing
     */
    public function getPublishLink(Request $request, int $id)
    {
        $visit = Visit::findOrFail($id);

        // Generate a secure token based on visit ID and created_at
        // Both view and register modes use the same token
        $token = hash('sha256', $visit->id . $visit->created_at . config('app.key'));

        // Generate the public links pointing to frontend routes
        $frontendUrl = config('app.frontend_url', 'http://localhost:4200');
        $viewLink = "{$frontendUrl}/public/visit/{$visit->id}?token={$token}";
        $registerLink = "{$frontendUrl}/visitor/register/{$visit->id}?token={$token}";

        return response()->json([
            'link' => $viewLink,
            'token' => $token,
            'registerToken' => $token,
            'registerLink' => $registerLink,
        ]);
    }

    /**
     * Convert 24-hour time to 12-hour AM/PM format
     */
    private function formatTimeTo12Hour(string $time): string
    {
        // Parse time (HH:MM:SS format)
        $parts = explode(':', $time);
        $hour = (int)$parts[0];
        $minute = $parts[1];

        // Convert to 12-hour format
        $amPm = $hour >= 12 ? 'PM' : 'AM';
        $hour12 = $hour % 12;
        if ($hour12 === 0) {
            $hour12 = 12;
        }

        return sprintf('%d:%s %s', $hour12, $minute, $amPm);
    }
}
