<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Builds and caches a flat, searchable snapshot of the site's own content
 * (treatments, packages, FAQs, rooms, guest house, pages, business facts)
 * for AiMatcher — entirely local, no external API involved. Rebuilt by
 * AdminController whenever content that feeds it is saved, or on demand
 * from the Settings page.
 */
final class AiKnowledge
{
    private const PATH = __DIR__ . '/../../storage/ai_knowledge.json';

    /** DB tables whose changes should trigger a rebuild (see AdminController). */
    public const WATCHED_TABLES = [
        'faqs', 'treatments', 'packages', 'rooms', 'guesthouse', 'guesthouse_units', 'pages',
    ];

    private const STOPWORDS = [
        'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'do', 'does', 'did',
        'i', 'you', 'your', 'yours', 'my', 'me', 'we', 'our', 'us', 'they', 'it', 'its',
        'what', 'when', 'where', 'who', 'which', 'how', 'can', 'could', 'would', 'will',
        'of', 'for', 'to', 'in', 'on', 'at', 'with', 'about', 'and', 'or', 'if', 'so',
        'please', 'hi', 'hello', 'hey', 'there', 'this', 'that', 'these', 'those',
        'have', 'has', 'had', 'get', 'got', 'also', 'just', 'like',
    ];

    /** Reads the cached snapshot, building it on first use. @return array<string, mixed> */
    public static function load(): array
    {
        $raw = @file_get_contents(self::PATH);
        $data = $raw !== false ? json_decode($raw, true) : null;
        return is_array($data) ? $data : self::rebuild();
    }

    /** Queries the DB fresh and writes the cached snapshot. @return array<string, mixed> */
    public static function rebuild(): array
    {
        $settings = [];
        foreach (Database::select('SELECT name, value FROM settings') as $row) {
            $settings[$row['name']] = (string) $row['value'];
        }

        $entries = [];
        $push = function (string $type, string $question, string $body, string $answer) use (&$entries): void {
            $answer = trim($answer);
            if ($answer === '') {
                return;
            }
            $entries[] = [
                'type'    => $type,
                'answer'  => $answer,
                'qTokens' => self::tokens($question),
                'bTokens' => self::tokens($body),
            ];
        };

        // --- Facts derived from business settings ---------------------------------
        $hours = json_decode($settings['hours'] ?? '[]', true) ?: [];
        if (is_array($hours) && $hours !== []) {
            $lines = [];
            foreach ($hours as $h) {
                if (is_array($h)) {
                    $lines[] = ($h['day'] ?? '') . ': ' . ($h['time'] ?? '');
                }
            }
            $push(
                'fact',
                'What are your opening hours? When are you open? What time do you close?',
                'hours opening closing schedule',
                "Our hours are:\n" . implode("\n", $lines),
            );
        }
        if (!empty($settings['address'])) {
            $push(
                'fact',
                'Where are you located? What is your address? How do I find you?',
                'address location directions map',
                "We're located at {$settings['address']}.",
            );
        }
        $contactBits = [];
        if (!empty($settings['phone'])) {
            $contactBits[] = "Phone: {$settings['phone']}";
        }
        if (!empty($settings['whatsapp'])) {
            $contactBits[] = "WhatsApp: {$settings['whatsapp']}";
        }
        if (!empty($settings['email'])) {
            $contactBits[] = "Email: {$settings['email']}";
        }
        if ($contactBits !== []) {
            $push(
                'fact',
                'What is your phone number? How can I contact you? What is your email or whatsapp?',
                'phone contact email whatsapp call number reach',
                implode("\n", $contactBits),
            );
        }
        $methods = json_decode($settings['payment_methods'] ?? '[]', true);
        if (is_array($methods) && $methods !== []) {
            $push(
                'fact',
                'What payment methods do you accept? Do you take cash or card?',
                'payment pay cash card visa mastercard bank transfer',
                'We accept: ' . implode(', ', $methods) . '.',
            );
        }
        $bookingBits = [];
        if (!empty($settings['whatsapp'])) {
            $bookingBits[] = "WhatsApp ({$settings['whatsapp']})";
        }
        if (!empty($settings['phone'])) {
            $bookingBits[] = "call {$settings['phone']}";
        }
        $push(
            'fact',
            'How do I book an appointment? Can you make a reservation for me? Can I book online?',
            'book booking reserve reservation appointment schedule',
            "I can't make bookings myself, but our team can — message us on "
                . ($bookingBits !== [] ? implode(' or ', $bookingBits) : 'WhatsApp or phone') . '.',
        );

        // --- FAQs -------------------------------------------------------------------
        foreach (Database::select('SELECT question, answer FROM faqs WHERE active = 1') as $f) {
            $push('faq', $f['question'], $f['question'], strip_tags($f['answer']));
        }

        // --- Treatments ---------------------------------------------------------------
        $rows = Database::select('SELECT name, duration, price, description FROM treatments WHERE status = 1');
        foreach ($rows as $t) {
            $push(
                'treatment',
                "How much is {$t['name']}? What is {$t['name']}? Tell me about the {$t['name']}. Price of {$t['name']}.",
                $t['name'] . ' ' . $t['description'],
                "{$t['name']} — {$t['duration']} — \${$t['price']}. " . mb_substr($t['description'], 0, 240),
            );
        }
        if ($rows !== []) {
            $names = array_map(static fn (array $t): string => $t['name'], $rows);
            $push(
                'treatments_list',
                'What treatments do you offer? What treatments are available? What services do you provide? '
                    . 'What can I book? What kind of massages do you have?',
                implode(', ', $names),
                'We offer: ' . implode(', ', $names) . '. Ask me about any one by name for its price and duration!',
            );
        }

        // --- Packages ------------------------------------------------------------------
        $rows = Database::select(
            'SELECT name, description, included, price, price_label, promo_price FROM packages WHERE active = 1',
        );
        foreach ($rows as $p) {
            $included = implode(', ', json_decode($p['included'] ?: '[]', true) ?: []);
            $price = $p['promo_price'] !== null
                ? "\${$p['promo_price']} (promo, normally \${$p['price']})"
                : "\${$p['price']}";
            $push(
                'package',
                "How much is the {$p['name']} package? What is included in {$p['name']}? Tell me about {$p['name']}.",
                $p['name'] . ' ' . $p['description'] . ' ' . $included,
                "{$p['name']} — {$price} {$p['price_label']}. " . mb_substr($p['description'], 0, 240)
                    . ($included !== '' ? " Includes: $included." : ''),
            );
        }
        if ($rows !== []) {
            $names = array_map(static fn (array $p): string => $p['name'], $rows);
            $push(
                'packages_list',
                'What packages do you offer? What packages are available? What deals or specials do you have?',
                implode(', ', $names),
                'Our packages: ' . implode(', ', $names) . '. Ask me about any one by name for details and pricing!',
            );
        }

        // --- Treatment rooms -------------------------------------------------------------
        foreach (Database::select('SELECT name, description FROM rooms WHERE active = 1') as $r) {
            $push(
                'room',
                "Tell me about the {$r['name']} room.",
                $r['name'] . ' ' . $r['description'],
                "{$r['name']}: " . mb_substr($r['description'], 0, 240),
            );
        }

        // --- Guest house -------------------------------------------------------------------
        $gh = Database::select('SELECT title, subtitle, description FROM guesthouse WHERE active = 1 LIMIT 1');
        if ($gh !== []) {
            $g = $gh[0];
            $push(
                'guesthouse',
                'Do you have a guest house? Can I stay overnight? Tell me about the guest house.',
                $g['title'] . ' ' . $g['subtitle'] . ' ' . strip_tags($g['description']),
                "{$g['title']} — {$g['subtitle']}. " . mb_substr(strip_tags($g['description']), 0, 300),
            );
        }
        $rows = Database::select(
            'SELECT name, subtitle, description, amenities, price_per_night, max_guests, bedrooms, bathrooms, min_nights
             FROM guesthouse_units WHERE active = 1',
        );
        foreach ($rows as $u) {
            $amenities = implode(', ', json_decode($u['amenities'] ?: '[]', true) ?: []);
            $push(
                'guesthouse_unit',
                "How much is {$u['name']} per night? Tell me about {$u['name']}. Guest house unit {$u['name']}.",
                $u['name'] . ' ' . $u['subtitle'] . ' ' . strip_tags($u['description']) . ' ' . $amenities,
                "{$u['name']} ({$u['subtitle']}) — \${$u['price_per_night']}/night, sleeps {$u['max_guests']}, "
                    . "{$u['bedrooms']} bed / {$u['bathrooms']} bath, min {$u['min_nights']} night(s)."
                    . ($amenities !== '' ? " Amenities: $amenities." : ''),
            );
        }

        // --- Other pages ---------------------------------------------------------------------
        $rows = Database::select(
            "SELECT title, subtitle, body FROM pages WHERE active = 1 AND slug NOT IN ('privacy', 'terms')",
        );
        foreach ($rows as $p) {
            $push(
                'page',
                $p['title'] . ' ' . $p['subtitle'],
                strip_tags($p['body']),
                mb_substr(strip_tags($p['body']), 0, 400),
            );
        }

        $data = [
            'built_at' => date('c'),
            'entries'  => $entries,
        ];

        $dir = dirname(self::PATH);
        if (!is_dir($dir)) {
            mkdir($dir, 0775, true);
        }
        file_put_contents(self::PATH, json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

        return $data;
    }

    /** @return string[] */
    public static function tokens(string $text): array
    {
        $text = strtolower(strip_tags($text));
        $text = preg_replace('/[^a-z0-9\s]/', ' ', $text) ?? '';
        $words = preg_split('/\s+/', trim($text)) ?: [];
        $words = array_filter($words, static fn (string $w): bool => mb_strlen($w) > 1 && !in_array($w, self::STOPWORDS, true));
        return array_values(array_unique($words));
    }
}
