<?php
// Prevent direct access
if (count(get_included_files()) === 1) {
    http_response_code(403);
    exit('Forbidden');
}

/**
 * DirectorioService — generación Gemini de contenido SEO del directorio
 * y helpers de listado.
 *
 * Uso principal:
 *   DirectorioService::generarDescripcion($db, $tenantId);
 *   // → actualiza descripcion_dir, descripcion_dir_en, dir_keywords en el tenant
 *
 *   DirectorioService::isOpenNow($hoursText, $timezone);
 *   // → bool aproximado basado en texto de horarios
 *
 *   DirectorioService::ciudades($db);
 *   // → ['Managua' => 5, 'Granada' => 2, ...]
 */
class DirectorioService {

    /** Rubros canónicos y sus etiquetas de display. */
    const RUBRO_LABELS = [
        'barberia'    => 'Barbería',
        'odontologia' => 'Odontología',
        'belleza'     => 'Belleza & Spa',
        'general'     => 'Servicios',
    ];

    /** Iconos SVG path (stroke) por rubro, para las tarjetas. */
    const RUBRO_ICONS = [
        'barberia'    => 'M8 3C8 3 7 7 7 9s1.5 3.5 3 4.5V20a1 1 0 002 0v-6.5c1.5-1 3-2.5 3-4.5s-1-6-1-6M10 3h4M9 9h6',
        'odontologia' => 'M12 2C9 5.5 7.5 9 7.5 12a4.5 4.5 0 009 0C16.5 9 15 5.5 12 2zm-1 10h2M9 17.5C10 20 11 21 12 21s2-1 3-3.5',
        'belleza'     => 'M12 3c-4.5 4-6 8-6 11a6 6 0 0012 0c0-3-1.5-7-6-11zm-2 9h4M8 17a4 4 0 008 0',
        'general'     => 'M8 7h12M8 12h12M8 17h12M3 7h.01M3 12h.01M3 17h.01',
    ];

    /**
     * Genera y persiste descripción ES/EN + keywords para el directorio.
     * Llama a Gemini dos veces (ES, EN) con temperatura baja.
     * No lanza excepciones — falla silenciosamente en producción.
     */
    public static function generarDescripcion(PDO $db, int $tenantId): bool {
        // Cargar datos del tenant
        $stmt = $db->prepare(
            "SELECT t.name, t.rubro, t.especialidad, t.ciudad, t.country,
                    GROUP_CONCAT(s.name ORDER BY s.id SEPARATOR ', ') AS servicios
             FROM tenants t
             LEFT JOIN services s ON s.tenant_id = t.id AND s.active = 1
             WHERE t.id = :id AND t.deleted_at IS NULL
             GROUP BY t.id LIMIT 1"
        );
        $stmt->execute([':id' => $tenantId]);
        $t = $stmt->fetch();
        if (!$t || !$t['name']) return false;

        $nombre    = $t['name'];
        $rubro     = self::RUBRO_LABELS[$t['rubro'] ?? 'general'] ?? 'negocio';
        $ciudad    = $t['ciudad'] ?? '';
        $servicios = $t['servicios'] ?? '';
        $espec     = $t['especialidad'] ?? '';

        try {
            $descEs = self::llamarGemini($nombre, $rubro, $espec, $ciudad, $servicios, 'español');
            $descEn = self::llamarGemini($nombre, $rubro, $espec, $ciudad, $servicios, 'English');
            $keywords = self::generarKeywords($nombre, $rubro, $ciudad, $servicios, $espec);

            $db->prepare(
                "UPDATE tenants
                 SET descripcion_dir    = :es,
                     descripcion_dir_en = :en,
                     dir_keywords       = :kw
                 WHERE id = :id"
            )->execute([':es' => $descEs, ':en' => $descEn, ':kw' => $keywords, ':id' => $tenantId]);

            return true;
        } catch (\Throwable $e) {
            error_log("[DirectorioService] generarDescripcion tenant=$tenantId: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Lista de ciudades con conteo de negocios activos para el índice del directorio.
     * Si $pais está definido, filtra por country. Retorna ['Managua' => 5, ...].
     */
    public static function ciudades(PDO $db, ?string $pais = null): array {
        $extra  = $pais ? " AND country = ?" : "";
        $params = $pais ? [$pais] : [];
        $stmt   = $db->prepare(
            "SELECT ciudad, COUNT(*) AS cnt
             FROM tenants
             WHERE visible_dir = 1
               AND onboarding_status = 'live'
               AND deleted_at IS NULL
               AND (billing_status IN ('free','active') OR billing_status IS NULL)
               AND ciudad IS NOT NULL AND ciudad != ''
               $extra
             GROUP BY ciudad
             ORDER BY cnt DESC, ciudad ASC"
        );
        $stmt->execute($params);
        $out = [];
        foreach ($stmt->fetchAll() as $r) {
            $out[$r['ciudad']] = (int)$r['cnt'];
        }
        return $out;
    }

    /**
     * Carga los negocios del directorio con filtros opcionales.
     * $filtros: ['ciudad' => 'Managua', 'rubro' => 'barberia', 'q' => 'texto']
     */
    public static function listar(PDO $db, array $filtros = [], int $limit = 60, int $offset = 0): array {
        $where  = ["t.visible_dir = 1", "t.onboarding_status = 'live'",
                   "t.deleted_at IS NULL",
                   "(t.billing_status IN ('free','active') OR t.billing_status IS NULL)",
                   "t.public_slug IS NOT NULL"];
        $params = [];

        if (!empty($filtros['country'])) {
            $where[]            = "t.country = :country";
            $params[':country'] = $filtros['country'];
        }
        if (!empty($filtros['ciudad'])) {
            $where[]           = "t.ciudad = :ciudad";
            $params[':ciudad'] = $filtros['ciudad'];
        }
        if (!empty($filtros['rubro'])) {
            $where[]          = "t.rubro = :rubro";
            $params[':rubro'] = $filtros['rubro'];
        }
        if (!empty($filtros['q'])) {
            $q = '%' . $filtros['q'] . '%';
            $where[]      = "(t.name LIKE :q1 OR t.ciudad LIKE :q2 OR t.dir_keywords LIKE :q3 OR t.descripcion_dir LIKE :q4)";
            $params[':q1'] = $q;
            $params[':q2'] = $q;
            $params[':q3'] = $q;
            $params[':q4'] = $q;
        }

        $sql = "SELECT t.id, t.name, t.public_slug, t.rubro, t.especialidad,
                       t.ciudad, t.country, t.timezone, t.descripcion_dir, t.descripcion_dir_en,
                       t.locale, t.banner_url, t.logo_url, t.color_primario,
                       vc.hours_json, vc.greeting,
                       t.created_at
                FROM tenants t
                LEFT JOIN vale_config vc ON vc.tenant_id = t.id
                WHERE " . implode(' AND ', $where) . "
                ORDER BY t.billing_status = 'active' DESC, t.created_at DESC
                LIMIT :limit OFFSET :offset";

        $stmt = $db->prepare($sql);
        foreach ($params as $k => $v) {
            $stmt->bindValue($k, $v);
        }
        $stmt->bindValue(':limit',  $limit,  PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $stmt->execute();
        $tenants = $stmt->fetchAll();

        // Adjuntar servicios destacados (máx 3 por negocio)
        $ids = array_column($tenants, 'id');
        $svcs = [];
        if ($ids) {
            $in = implode(',', array_map('intval', $ids));
            $svcRows = $db->query(
                "SELECT tenant_id, name FROM services
                 WHERE tenant_id IN ($in) AND active = 1
                 ORDER BY tenant_id, id LIMIT " . (count($ids) * 3)
            )->fetchAll();
            foreach ($svcRows as $s) {
                if (!isset($svcs[$s['tenant_id']]) || count($svcs[$s['tenant_id']]) < 3) {
                    $svcs[$s['tenant_id']][] = $s['name'];
                }
            }
        }

        foreach ($tenants as &$t) {
            $t['servicios_destacados'] = $svcs[$t['id']] ?? [];
            $t['rubro_label'] = self::RUBRO_LABELS[$t['rubro'] ?? 'general'] ?? 'Negocio';
            $t['rubro_icon']  = self::RUBRO_ICONS[$t['rubro'] ?? 'general']  ?? self::RUBRO_ICONS['general'];
            $t['estado']      = self::isOpenNow(
                json_decode($t['hours_json'] ?? '{}', true)['texto'] ?? '',
                $t['timezone'] ?: 'America/Managua'
            );
        }
        unset($t);

        return $tenants;
    }

    /**
     * Determina si el negocio está abierto ahora mismo.
     * Parseo aproximado del texto libre de horarios (ej. "Lun-Vie 9-18, Sáb 9-13").
     * Devuelve 'abierto' | 'cerrado' | 'desconocido'.
     */
    public static function isOpenNow(string $hoursText, string $timezone = 'America/Managua'): string {
        if (!$hoursText) return 'desconocido';

        try {
            $now  = new DateTime('now', new DateTimeZone($timezone));
        } catch (\Exception) {
            $now = new DateTime('now');
        }

        $dayNum   = (int)$now->format('N'); // 1=Lun ... 7=Dom
        $hourNow  = (int)$now->format('G') + (int)$now->format('i') / 60;

        $esDay = ['lun'=>1,'mar'=>2,'mié'=>3,'mie'=>3,'jue'=>4,'vie'=>5,'sáb'=>6,'sab'=>6,'dom'=>7];

        // Dividir por coma → segmentos
        foreach (explode(',', strtolower($hoursText)) as $seg) {
            $seg = trim($seg);
            // Detectar rango de días: "lun-vie 9-18" o "lun a vie 9-18"
            if (preg_match('/([a-záéíóúü]+)[- a]+([a-záéíóúü]+)\s+(\d+)[:\.]?(\d*)\s*[-a]\s*(\d+)[:\.]?(\d*)/', $seg, $m)) {
                $d1 = $esDay[$m[1]] ?? null;
                $d2 = $esDay[$m[2]] ?? null;
                if ($d1 && $d2 && $dayNum >= $d1 && $dayNum <= $d2) {
                    $open  = (int)$m[3] + ((int)$m[4]  / 60);
                    $close = (int)$m[5] + ((int)$m[6] / 60);
                    if ($hourNow >= $open && $hourNow < $close) return 'abierto';
                    return 'cerrado';
                }
            }
            // Día suelto: "sáb 9-13"
            if (preg_match('/^([a-záéíóúü]+)\s+(\d+)[:\.]?(\d*)\s*[-a]\s*(\d+)[:\.]?(\d*)/', $seg, $m)) {
                $d = $esDay[$m[1]] ?? null;
                if ($d && $d === $dayNum) {
                    $open  = (int)$m[2] + ((int)$m[3] / 60);
                    $close = (int)$m[4] + ((int)$m[5] / 60);
                    if ($hourNow >= $open && $hourNow < $close) return 'abierto';
                    return 'cerrado';
                }
            }
        }
        return 'desconocido';
    }

    /**
     * Genera el XML del sitemap para el directorio.
     * Incluye /directorio, /directorio/{ciudad}, /directorio/{ciudad}/{rubro}, /n/{slug}.
     */
    public static function sitemap(PDO $db, string $baseUrl): string {
        $urls = [];

        // Country-scoped directorio pages
        $paisSlugs = ['ni' => 'NI', 'hn' => 'HN', 'sv' => 'SV', 'us' => 'US'];
        foreach ($paisSlugs as $slug => $iso) {
            $urls[] = "$baseUrl/$slug";
            foreach (array_keys(self::ciudades($db, $iso)) as $c) {
                $cs     = self::slugify($c);
                $urls[] = "$baseUrl/$slug/$cs";
                foreach (array_keys(self::RUBRO_LABELS) as $r) {
                    $urls[] = "$baseUrl/$slug/$cs/$r";
                }
            }
        }

        // Páginas públicas de negocios
        $tenants = $db->query(
            "SELECT public_slug, updated_at FROM tenants
             WHERE onboarding_status = 'live' AND visible_dir = 1
               AND deleted_at IS NULL
               AND public_slug IS NOT NULL
             ORDER BY id DESC LIMIT 500"
        )->fetchAll();
        foreach ($tenants as $t) {
            $urls[] = "$baseUrl/n/{$t['public_slug']}";
        }

        $xml  = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
        foreach ($urls as $u) {
            $xml .= "  <url><loc>" . htmlspecialchars($u) . "</loc></url>\n";
        }
        $xml .= '</urlset>';
        return $xml;
    }

    /** Convierte texto a slug URL-safe. */
    public static function slugify(string $text): string {
        $text = mb_strtolower($text, 'UTF-8');
        $map  = ['á'=>'a','é'=>'e','í'=>'i','ó'=>'o','ú'=>'u','ü'=>'u','ñ'=>'n',' '=>'-'];
        $text = strtr($text, $map);
        $text = preg_replace('/[^a-z0-9\-]/', '', $text);
        return preg_replace('/-+/', '-', trim($text, '-'));
    }

    /** Convierte slug a texto para mostrar (capitaliza). */
    public static function deslugify(string $slug): string {
        return implode(' ', array_map('ucfirst', explode('-', $slug)));
    }

    // ── Helpers privados ──────────────────────────────────────────────────────

    private static function llamarGemini(
        string $nombre, string $rubro, string $espec,
        string $ciudad, string $servicios, string $idioma
    ): string {
        $apiKey = getenv('GEMINI_API_KEY');
        if (!$apiKey) return '';

        $ctx  = $espec ? "$rubro ($espec)" : $rubro;
        $loc  = $ciudad ? " en $ciudad" : '';
        $svcs = $servicios ? " Servicios: $servicios." : '';

        $prompt = "Escribí una descripción breve y atractiva (máx 2 frases) para el directorio, "
            . "en $idioma, de este negocio. SOLO el texto, sin comillas ni aclaraciones. "
            . "Negocio: $nombre, rubro $ctx$loc.$svcs";

        $body = json_encode([
            'contents' => [['role' => 'user', 'parts' => [['text' => $prompt]]]],
            'generationConfig' => ['temperature' => 0.4, 'maxOutputTokens' => 120],
        ]);

        $model = 'gemini-2.0-flash';
        $url   = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}";

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $body,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
            CURLOPT_TIMEOUT        => 8,
        ]);
        $resp = curl_exec($ch);
        curl_close($ch);

        $data = json_decode($resp, true);
        $text = trim($data['candidates'][0]['content']['parts'][0]['text'] ?? '');
        return mb_substr($text, 0, 400);
    }

    private static function generarKeywords(
        string $nombre, string $rubro, string $ciudad,
        string $servicios, string $espec
    ): string {
        $parts = array_filter(array_map('trim', [
            mb_strtolower($nombre),
            mb_strtolower($rubro),
            mb_strtolower($ciudad),
            mb_strtolower($espec),
            mb_strtolower($servicios),
        ]));
        return implode(', ', $parts);
    }
}
