<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\BaseController;
use App\Core\Database;
use App\Core\Request;
use App\Core\Response;
use App\Helpers\AjaxResponse;
use App\Models\AppointmentModel;
use App\Services\AppointmentService;
use App\Services\ExportService;

final class AppointmentController extends BaseController
{
    public function calendar(Request $request): Response
    {
        return $this->view('appointments/calendar', [
            'title' => 'Appointments Calendar',
            'activeNav' => 'appointments',
            'breadcrumbs' => [['Home', 'dashboard'], ['Scheduling', 'appointments'], ['Calendar', 'appointments']],
            'lookups' => $this->lookups(),
        ]);
    }

    /** JSON feed the calendar view's JS polls for a given date range — supports day/week/month by just varying start/end. */
    public function feed(Request $request): Response
    {
        $start = $request->query('start');
        $end = $request->query('end');

        if (!$start || !$end) {
            return AjaxResponse::error('start and end query parameters are required (Y-m-d H:i:s).', 422);
        }

        $filters = [
            'business_line_id' => $request->query('business_line_id'),
            'assigned_staff_id' => $request->query('assigned_staff_id'),
            'status' => $request->query('status'),
        ];

        $rows = (new AppointmentModel())->inRange($start, $end, $filters);

        return AjaxResponse::success($rows);
    }

    /** Naive availability check: is this staff member free for this exact window? Used by the booking form before submit. */
    public function checkAvailability(Request $request): Response
    {
        $staffId = $request->query('assigned_staff_id');
        $start = $request->query('start');
        $end = $request->query('end');

        if (!$staffId || !$start || !$end) {
            return AjaxResponse::error('assigned_staff_id, start, and end query parameters are all required.', 422);
        }

        $excludeId = $request->query('exclude_id') ? (int) $request->query('exclude_id') : null;

        $conflict = (new AppointmentModel())->hasConflict((int) $staffId, (string) $start, (string) $end, $excludeId);

        return AjaxResponse::success(['available' => !$conflict]);
    }

    public function store(Request $request): Response
    {
        $data = $this->validateOrFail($request, [
            'business_line_id' => 'required|integer|exists:business_lines,id',
            'customer_id' => 'nullable|integer|exists:customers,id',
            'lead_id' => 'nullable|integer|exists:leads,id',
            'service_type_id' => 'nullable|integer|exists:service_types,id',
            'assigned_staff_id' => 'nullable|integer|exists:users,id',
            'scheduled_start' => 'required|date',
            'scheduled_end' => 'required|date',
            'location_address' => 'nullable|string|max:255',
            'notes' => 'nullable|string',
        ]);

        if (empty($data['customer_id']) && empty($data['lead_id'])) {
            return AjaxResponse::validationError(['customer_id' => ['An appointment must be linked to a customer or a lead.']]);
        }

        $data['status'] = 'scheduled';

        $recurrence = $request->input('recurrence');
        if (is_array($recurrence) && !empty($recurrence['frequency'])) {
            $data['recurrence_rule'] = $recurrence;
        }

        $result = (new AppointmentService())->book($data, $this->currentUserId());

        if (!$result['success']) {
            return AjaxResponse::error($result['message'], 409, ['conflict' => true]);
        }

        return AjaxResponse::created(['id' => $result['id']], $result['message']);
    }

    public function reschedule(Request $request, array $params): Response
    {
        $data = $this->validateOrFail($request, [
            'scheduled_start' => 'required|date',
            'scheduled_end' => 'required|date',
        ]);

        $result = (new AppointmentService())->reschedule((int) $params['id'], $data['scheduled_start'], $data['scheduled_end'], $this->currentUserId());

        return $result['success'] ? AjaxResponse::success(null, $result['message']) : AjaxResponse::error($result['message'], 409);
    }

    public function cancel(Request $request, array $params): Response
    {
        $reason = (string) $request->input('reason', 'No reason given');
        (new AppointmentService())->cancel((int) $params['id'], $reason, $this->currentUserId());

        return AjaxResponse::success(null, 'Appointment cancelled.');
    }

    public function complete(Request $request, array $params): Response
    {
        (new AppointmentService())->markCompleted((int) $params['id'], $this->currentUserId());

        return AjaxResponse::success(null, 'Appointment marked completed.');
    }

    public function missed(Request $request, array $params): Response
    {
        (new AppointmentService())->markMissed((int) $params['id'], $this->currentUserId());

        return AjaxResponse::success(null, 'Appointment marked as missed.');
    }

    public function destroy(Request $request, array $params): Response
    {
        (new AppointmentModel())->delete((int) $params['id'], $this->currentUserId());

        return AjaxResponse::success(null, 'Appointment deleted.');
    }

    public function export(Request $request): Response
    {
        $start = $request->query('start', date('Y-m-01'));
        $end = $request->query('end', date('Y-m-t', strtotime($start)));

        $csv = (new ExportService())->exportAppointments($start, $end, [
            'business_line_id' => $request->query('business_line_id'),
        ]);

        return Response::html($csv)
            ->withHeader('Content-Type', 'text/csv; charset=UTF-8')
            ->withHeader('Content-Disposition', 'attachment; filename="appointments-' . date('Y-m-d') . '.csv"');
    }

    private function lookups(): array
    {
        $pdo = Database::connection();

        return [
            'business_lines' => $pdo->query('SELECT id, name FROM business_lines ORDER BY name')->fetchAll(),
            'staff' => $pdo->query("SELECT id, CONCAT(first_name, ' ', last_name) AS name FROM users WHERE deleted_at IS NULL AND status = 'active' ORDER BY first_name")->fetchAll(),
            'service_types' => $pdo->query('SELECT id, name, business_line_id, default_duration_minutes FROM service_types WHERE is_active = 1')->fetchAll(),
            'customers' => (new \App\Models\CustomerModel())->forSelect(),
            'leads' => (new \App\Models\LeadModel())->forSelect(),
        ];
    }
}
