<?php

namespace Modules\WhatsApp\Entities\Services\Tools;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Modules\AiCore\Entities\Contracts\AiTool;

/**
 * Guarded "book appointment" action for the AI — the reference AiTool.
 *
 * SECURITY MODEL: the AI never runs SQL. It can only request this one
 * whitelisted action with structured args; THIS class validates every argument
 * and performs a single, safe, parameterised insert:
 *   - the customer is resolved from the WhatsApp number (NOT from AI input);
 *   - the pet must be one of THAT customer's own pets;
 *   - the date is parsed and bounded (tomorrow .. +N days);
 *   - blocked owners are rejected;
 *   - the appointment is created as 'pending' for staff to confirm — the AI
 *     never finalises a slot, picks a doctor, or touches anything else.
 * Unknown actions/args are ignored. There is no path to delete/modify data.
 *
 * Implements AiCore's AiTool contract so it can be registered on the shared
 * AiToolRegistry and dispatched by the ToolRunner.
 */
class AppointmentBookingTool implements AiTool
{
    /** Max days ahead an AI booking may be requested. */
    private int $maxDaysAhead = 90;

    public function name(): string
    {
        return 'book_appointment';
    }

    public function description(): string
    {
        return "Request a PENDING appointment for one of the customer's own pets (staff confirm the exact slot). "
            . "Use only when the customer clearly wants to book.";
    }

    public function argsSchema(): array
    {
        return [
            'pet'    => 'pet name from the context, or empty',
            'date'   => 'YYYY-MM-DD (required)',
            'time'   => 'HH:MM (optional)',
            'reason' => 'short reason (optional)',
        ];
    }

    /**
     * @param array $context expects ['wa_id' => string]
     * @return array{ok:bool, reply:string, meta:array}
     */
    public function handle(int $businessId, array $args, array $context = []): array
    {
        $waId = (string) ($context['wa_id'] ?? '');

        if (!class_exists(\Modules\VetClinic\Entities\VcAppointment::class)
            || !class_exists(\Modules\VetClinic\Entities\VcPet::class)) {
            return $this->fail("Thanks! A team member will help you book that shortly.");
        }

        $contactId = $this->resolveContactId($businessId, $waId);
        if (!$contactId) {
            return $this->fail("I couldn't find your profile from this number, so a team member will reach out to book for you. 🙏");
        }

        // Respect owner blocks.
        if (class_exists(\Modules\VetClinic\Services\OwnerBlockService::class)) {
            try {
                if (app(\Modules\VetClinic\Services\OwnerBlockService::class)->isBlockedContact($businessId, $contactId)) {
                    return $this->fail("I'm unable to book online for this account. Please contact the clinic directly.");
                }
            } catch (\Throwable $e) {
            }
        }

        // Resolve the pet — ONLY among this customer's own pets.
        $pets = \Modules\VetClinic\Entities\VcPet::where('business_id', $businessId)
            ->where('contact_id', $contactId)->get(['id', 'petname']);
        if ($pets->isEmpty()) {
            return $this->fail("I couldn't find a pet on your profile. A team member will help you register and book. 🐾");
        }

        $pet = $this->matchPet($pets, (string) ($args['pet'] ?? ''));
        if (!$pet) {
            $names = $pets->pluck('petname')->implode(', ');
            return $this->clarify("Which pet is this appointment for? You have: {$names}.");
        }

        // Validate the date (must be a real date, tomorrow .. +N days).
        $date = $this->parseDate((string) ($args['date'] ?? ''));
        if (!$date) {
            return $this->clarify("What date would you like? Please give a day within the next {$this->maxDaysAhead} days (e.g. 2026-06-10).");
        }

        $time = $this->parseTime((string) ($args['time'] ?? ''));
        $reason = mb_substr(trim((string) ($args['reason'] ?? '')), 0, 250);

        // Dedupe: don't create a second pending request for the same pet+date.
        $exists = \Modules\VetClinic\Entities\VcAppointment::where('business_id', $businessId)
            ->where('contact_id', $contactId)->where('pet_id', $pet->id)
            ->whereDate('appointment_date', $date->toDateString())
            ->where('status', 'pending')->exists();
        if ($exists) {
            return [
                'ok'    => false,
                'reply' => "You already have a pending request for {$pet->petname} on {$date->format('D, d M Y')}. Our team will confirm it soon. 👍",
                'meta'  => ['label' => 'booking-request'],
            ];
        }

        // Create the pending appointment (staff confirm).
        $appointment = new \Modules\VetClinic\Entities\VcAppointment();
        $appointment->forceFill([
            'business_id'      => $businessId,
            'contact_id'       => $contactId,
            'pet_id'           => $pet->id,
            'appointment_date' => $date->toDateString(),
            'appointment_time' => $time,
            'ref_no'           => 'APT-' . strtoupper(uniqid()),
            'status'           => 'pending',
            'reason'           => $reason ?: 'Requested via WhatsApp',
            'notes'            => 'Auto-created from a WhatsApp AI request — pending staff confirmation.',
        ]);

        // Honour the pet's preferred doctor if configured (no AI choice).
        try {
            $preferred = method_exists($pet, 'effectivePreferredDoctorId') ? $pet->effectivePreferredDoctorId() : null;
            if ($preferred) {
                $appointment->doctor_id = $preferred;
            }
        } catch (\Throwable $e) {
        }

        $appointment->save();

        return [
            'ok'    => true,
            'reply' => "✅ I've requested an appointment for *{$pet->petname}* on *{$date->format('D, d M Y')}* at *{$time}* (ref {$appointment->ref_no}). "
                . "Our team will confirm the exact slot shortly. Reply here if you'd like to change anything.",
            'meta'  => ['label' => 'booking-request'],
        ];
    }

    private function matchPet($pets, string $name)
    {
        $name = mb_strtolower(trim($name));
        if ($name !== '') {
            foreach ($pets as $p) {
                if (str_contains(mb_strtolower((string) $p->petname), $name)) {
                    return $p;
                }
            }
            return null; // a name was given but didn't match → ask
        }
        return $pets->count() === 1 ? $pets->first() : null;
    }

    private function parseDate(string $raw): ?Carbon
    {
        $raw = trim($raw);
        if ($raw === '') {
            return null;
        }
        try {
            $d = Carbon::parse($raw)->startOfDay();
        } catch (\Throwable $e) {
            return null;
        }
        $min = Carbon::tomorrow()->startOfDay();
        $max = Carbon::today()->addDays($this->maxDaysAhead)->endOfDay();
        if ($d->lt($min) || $d->gt($max)) {
            return null;
        }
        return $d;
    }

    private function parseTime(string $raw): string
    {
        $raw = trim($raw);
        if ($raw !== '' && preg_match('/^([01]?\d|2[0-3]):([0-5]\d)/', $raw, $m)) {
            return sprintf('%02d:%02d:00', (int) $m[1], (int) $m[2]);
        }
        return '10:00:00';
    }

    private function resolveContactId(int $businessId, string $waId): ?int
    {
        $national = preg_replace('/^20/', '', $waId);
        if (strlen($national) < 7) {
            return null;
        }
        return DB::table('contacts')->where('business_id', $businessId)
            ->where('mobile', 'like', "%{$national}%")
            ->value('id');
    }

    private function fail(string $reply): array
    {
        return ['ok' => false, 'reply' => $reply, 'meta' => ['label' => 'needs-staff']];
    }

    private function clarify(string $reply): array
    {
        return ['ok' => false, 'reply' => $reply, 'meta' => ['label' => null]];
    }
}
