<?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\LeadModel;
use App\Models\TagModel;
use App\Services\ActivityLogger;
use App\Services\AppointmentService;

/**
 * PublicLeadController
 *
 * Public, unauthenticated lead capture for marketing-site forms — the
 * "Get a Free Quote" style forms (capture()) and the multi-step
 * booking wizard (bookQuote()) — mirroring exactly how the Chatbot
 * widget endpoints work: no session, CORS-gated to trusted origins
 * only, rate-limited. Intentionally separate from the authenticated
 * external API (/api/v1/external/leads, which needs an API key) — a
 * public marketing form can never safely hold a secret key, so it
 * needs its own always-open-but-restricted-by-origin door.
 *
 * Reuses the exact same duplicate-detection LeadModel already uses
 * everywhere else (chatbot capture, CSV import, manual entry), so a
 * repeat visitor filling a form out twice doesn't create two leads.
 */
final class PublicLeadController extends BaseController
{
    public function capture(Request $request): Response
    {
        $data = $this->validateOrFail($request, [
            'business_line_id' => 'required|integer|exists:business_lines,id',
            'fullName' => 'required|string|max:150',
            'email' => 'nullable|email|max:191',
            'phone' => 'nullable|string|max:30',
            'city' => 'nullable|string|max:120',
            'propertyType' => 'nullable|string|max:60',
            'propertySize' => 'nullable|string|max:60',
            'frequency' => 'nullable|string|max:60',
            'preferredDate' => 'nullable|string|max:20',
            'serviceInterest' => 'nullable|string|max:120',
            'leadType' => 'nullable|string|max:60',
            'message' => 'nullable|string|max:2000',
        ]);

        if (empty($data['email']) && empty($data['phone'])) {
            return AjaxResponse::error('Please provide an email or phone number.', 422);
        }

        [$leadId, ] = $this->resolveLead($data, 'Submitted via website quote form.', [
            'serviceInterest' => 'Service', 'leadType' => 'Type', 'city' => 'City', 'propertyType' => 'Property type',
            'propertySize' => 'Size', 'frequency' => 'Frequency', 'preferredDate' => 'Preferred date',
        ]);

        return AjaxResponse::created(['lead_id' => $leadId], 'Thanks — we have your details and will follow up shortly.');
    }

    /**
     * The multi-step booking wizard — the visitor picked a specific
     * date/time for a quote visit, so this creates a real Appointment
     * (not just a note) on top of the same lead-capture logic
     * capture() uses. appointments.lead_id exists specifically for
     * this: "pre-sale assessments booked before conversion."
     */
    public function bookQuote(Request $request): Response
    {
        $data = $this->validateOrFail($request, [
            'business_line_id' => 'required|integer|exists:business_lines,id',
            'fullName' => 'required|string|max:150',
            'email' => 'nullable|email|max:191',
            'phone' => 'nullable|string|max:30',
            'city' => 'nullable|string|max:120',
            'service' => 'nullable|string|max:120',
            'date' => 'required|string|max:20',
            'time' => 'required|string|max:20',
            'patientName' => 'nullable|string|max:150',
            'relationship' => 'nullable|string|max:100',
            'notes' => 'nullable|string|max:2000',
        ]);

        if (empty($data['email']) && empty($data['phone'])) {
            return AjaxResponse::error('Please provide an email or phone number.', 422);
        }

        $start = strtotime("{$data['date']} {$data['time']}");
        if ($start === false) {
            return AjaxResponse::error('Invalid date or time.', 422);
        }
        $scheduledStart = date('Y-m-d H:i:s', $start);
        $scheduledEnd = date('Y-m-d H:i:s', $start + 3600); // 60-minute quote visit, matches the slot spacing already used on the booking page

        [$leadId, ] = $this->resolveLead(
            array_merge($data, ['serviceInterest' => $data['service'] ?? null, 'message' => $data['notes'] ?? null]),
            'Booked via website quote-visit scheduler.',
            ['service' => 'Service', 'city' => 'City', 'patientName' => 'Business name', 'relationship' => 'Type of space']
        );

        $result = (new AppointmentService())->book([
            'business_line_id' => (int) $data['business_line_id'],
            'customer_id' => null,
            'lead_id' => $leadId,
            'scheduled_start' => $scheduledStart,
            'scheduled_end' => $scheduledEnd,
            'status' => 'scheduled',
            'location_address' => $data['city'] ?? null,
            'notes' => $this->buildBookingNote($data),
        ], null);

        if (!($result['success'] ?? false)) {
            return AjaxResponse::error($result['message'] ?? 'Could not book that time — please call us to confirm.', 422);
        }

        $this->tagServiceInterest($leadId, $data['service'] ?? null);

        return AjaxResponse::created(
            ['lead_id' => $leadId, 'appointment_id' => $result['id'] ?? null],
            'Your quote visit is booked — we will confirm details before your visit.'
        );
    }

    /**
     * Finds an existing lead (by email/phone) or creates a new one,
     * always leaving a real Notes-tab entry either way — shared by
     * capture() and bookQuote() so both forms behave identically for
     * "is this visitor already a lead?".
     *
     * @return array{0:int,1:bool} [leadId, wasNewlyCreated]
     */
    private function resolveLead(array $data, string $noteIntro, array $noteFieldLabels): array
    {
        $leads = new LeadModel();
        $duplicates = $leads->findPotentialDuplicates($data['email'] ?? null, $data['phone'] ?? null);

        if ($duplicates !== []) {
            $leadId = (int) $duplicates[0]['id'];
            $this->appendNote($leadId, $noteIntro, $data, $noteFieldLabels);
            ActivityLogger::record('lead', $leadId, 'lead_updated', 'New inquiry from an existing lead (website form)', category: 'system');

            return [$leadId, false];
        }

        $nameParts = explode(' ', trim($data['fullName']), 2);
        $newStageId = Database::connection()->query("SELECT id FROM lead_stages WHERE slug = 'new' LIMIT 1")->fetchColumn();

        $leadId = $leads->insert([
            'business_line_id' => $data['business_line_id'],
            'lead_stage_id' => (int) $newStageId,
            'first_name' => $nameParts[0],
            'last_name' => $nameParts[1] ?? null,
            'email' => $data['email'] ?? null,
            'phone' => $data['phone'] ?? null,
            'priority' => 'medium',
            'currency' => 'CAD',
        ]);

        // The Lead Detail page's "Notes" tab reads from the polymorphic
        // notes table (via NoteModel), not the leads.notes column — so
        // the actual visible note has to be created explicitly here.
        $this->appendNote($leadId, $noteIntro, $data, $noteFieldLabels);
        ActivityLogger::record('lead', $leadId, 'lead_created', 'Lead captured via website form', category: 'system');

        return [$leadId, true];
    }

    /** Tags the lead with whichever service it's interested in (e.g. "Residential Cleaning", "Office Cleaning") — filterable/reportable on the Leads list, unlike free-text notes. Works uniformly across every form since it's driven entirely by that form's own field, no per-page CRM setup needed. */
    private function tagServiceInterest(int $leadId, ?string $serviceInterest): void
    {
        if (empty($serviceInterest)) {
            return;
        }

        $tagId = (new TagModel())->findOrCreateByName($serviceInterest);
        (new TagModel())->attach($tagId, 'lead', $leadId);
    }

    private function buildNote(string $intro, array $d, array $fieldLabels): string
    {
        $lines = [$intro];
        foreach ($fieldLabels as $key => $label) {
            if (!empty($d[$key])) {
                $lines[] = "{$label}: {$d[$key]}";
            }
        }
        if (!empty($d['message'])) {
            $lines[] = "Message: {$d['message']}";
        }

        return implode("\n", $lines);
    }

    private function buildBookingNote(array $d): string
    {
        $lines = ['Quote visit booked via website.'];
        foreach (['patientName' => 'Business name', 'relationship' => 'Type of space'] as $key => $label) {
            if (!empty($d[$key])) {
                $lines[] = "{$label}: {$d[$key]}";
            }
        }
        if (!empty($d['notes'])) {
            $lines[] = "Notes: {$d['notes']}";
        }

        return implode("\n", $lines);
    }

    private function appendNote(int $leadId, string $intro, array $data, array $fieldLabels): void
    {
        Database::connection()->prepare(
            'INSERT INTO notes (entity_type, entity_id, body, created_at) VALUES (\'lead\', :id, :body, NOW())'
        )->execute(['id' => $leadId, 'body' => $this->buildNote($intro, $data, $fieldLabels)]);
    }
}