<?php

namespace App\Services\Ai;

/**
 * Curated font pairings — the typography foundation for AI generation.
 *
 * The model never picks a font. It picks a personality (or inherits the palette's
 * recommended pairing); we map to one of these hand-vetted pairings. Every family
 * is in the editor's Google-Fonts library (resources/js/editor/fonts.ts) and is
 * loaded by the export renderer (`ensureFontsForLayout`), so each one renders
 * identically in the editor and the exported ZIP — no silent fallback.
 *
 * Each pairing supplies the per-role typography tuples the generator already
 * consumes (`Styles`-shaped `text` map): headline / subhead / body / eyebrow /
 * stat / caption. Weights are restricted to those each family actually ships.
 */
class FontPairings
{
    /**
     * @var array<string, array{name: string, personality: string, text: array<string, array<string, mixed>>}>
     */
    private const PAIRINGS = [
        // Elegant serif display + clean sans body. Editorial / wellness / luxury.
        'editorial-serif' => [
            'name' => 'Editorial Serif', 'personality' => 'calm, premium, content-led',
            'text' => [
                'headline' => ['font' => 'Instrument Serif', 'weight' => 400, 'lineHeight' => 1.0, 'min' => 64, 'max' => 200, 'spacing' => -1.0, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',            'weight' => 400, 'lineHeight' => 1.4, 'min' => 36, 'max' => 60,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',            'weight' => 400, 'lineHeight' => 1.5, 'min' => 34, 'max' => 56,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Inter',            'weight' => 600, 'lineHeight' => 1.0, 'min' => 28, 'max' => 42,  'spacing' => 2.0,  'case' => 'upper'],
                'stat'     => ['font' => 'Instrument Serif', 'weight' => 400, 'lineHeight' => 1.0, 'min' => 90, 'max' => 200, 'spacing' => -1.0, 'case' => 'none'],
                'caption'  => ['font' => 'Inter',            'weight' => 500, 'lineHeight' => 1.3, 'min' => 32, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
            ],
        ],
        // Bold geometric grotesque, tight tracking. Tech / SaaS / fintech.
        'modern-grotesk' => [
            'name' => 'Modern Grotesk', 'personality' => 'confident, geometric, clean',
            'text' => [
                'headline' => ['font' => 'Space Grotesk', 'weight' => 700, 'lineHeight' => 0.98, 'min' => 60, 'max' => 130, 'spacing' => -2.0, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',         'weight' => 400, 'lineHeight' => 1.35, 'min' => 28, 'max' => 52,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',         'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Space Grotesk', 'weight' => 700, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 2.5,  'case' => 'upper'],
                'stat'     => ['font' => 'Space Grotesk', 'weight' => 700, 'lineHeight' => 0.96, 'min' => 80, 'max' => 150, 'spacing' => -2.0, 'case' => 'none'],
                'caption'  => ['font' => 'Space Grotesk', 'weight' => 500, 'lineHeight' => 1.1,  'min' => 32, 'max' => 54,  'spacing' => -0.5, 'case' => 'none'],
            ],
        ],
        // Rounded approachable sans. Social / health / kids / consumer.
        'soft-rounded' => [
            'name' => 'Soft Rounded', 'personality' => 'warm, friendly, approachable',
            'text' => [
                'headline' => ['font' => 'Poppins',     'weight' => 700, 'lineHeight' => 1.05, 'min' => 56, 'max' => 120, 'spacing' => -1.0, 'case' => 'none'],
                'subhead'  => ['font' => 'Nunito Sans', 'weight' => 400, 'lineHeight' => 1.4,  'min' => 28, 'max' => 52,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Nunito Sans', 'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Poppins',     'weight' => 600, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 2.0,  'case' => 'upper'],
                'stat'     => ['font' => 'Poppins',     'weight' => 700, 'lineHeight' => 0.98, 'min' => 80, 'max' => 148, 'spacing' => -1.0, 'case' => 'none'],
                'caption'  => ['font' => 'Poppins',     'weight' => 600, 'lineHeight' => 1.1,  'min' => 32, 'max' => 54,  'spacing' => -0.5, 'case' => 'none'],
            ],
        ],
        // High-drama serif (Playfair) + neutral sans. Fashion / luxury / premium.
        'luxe-serif' => [
            'name' => 'Luxe Serif', 'personality' => 'elegant, high-contrast, refined',
            'text' => [
                'headline' => ['font' => 'Playfair Display', 'weight' => 700, 'lineHeight' => 1.02, 'min' => 56, 'max' => 150, 'spacing' => -0.5, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',            'weight' => 400, 'lineHeight' => 1.4,  'min' => 28, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',            'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Inter',            'weight' => 600, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 2.5,  'case' => 'upper'],
                'stat'     => ['font' => 'Playfair Display', 'weight' => 700, 'lineHeight' => 1.0,  'min' => 80, 'max' => 150, 'spacing' => -0.5, 'case' => 'none'],
                'caption'  => ['font' => 'Inter',            'weight' => 500, 'lineHeight' => 1.3,  'min' => 32, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
            ],
        ],
        // Contemporary humanist sans, both roles. Productivity / modern SaaS.
        'clean-jakarta' => [
            'name' => 'Clean Jakarta', 'personality' => 'modern, neutral, trustworthy',
            'text' => [
                'headline' => ['font' => 'Plus Jakarta Sans', 'weight' => 700, 'lineHeight' => 1.05, 'min' => 56, 'max' => 120, 'spacing' => -1.0, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',             'weight' => 400, 'lineHeight' => 1.4,  'min' => 28, 'max' => 52,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',             'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Plus Jakarta Sans', 'weight' => 600, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 2.0,  'case' => 'upper'],
                'stat'     => ['font' => 'Plus Jakarta Sans', 'weight' => 700, 'lineHeight' => 0.98, 'min' => 80, 'max' => 148, 'spacing' => -1.0, 'case' => 'none'],
                'caption'  => ['font' => 'Plus Jakarta Sans', 'weight' => 600, 'lineHeight' => 1.1,  'min' => 32, 'max' => 54,  'spacing' => -0.5, 'case' => 'none'],
            ],
        ],
        // Heavy display headline (Archivo Black) + clean body. Punchy consumer.
        'bold-display' => [
            'name' => 'Bold Display', 'personality' => 'punchy, loud, high-impact',
            'text' => [
                'headline' => ['font' => 'Archivo Black', 'weight' => 400, 'lineHeight' => 0.98, 'min' => 54, 'max' => 116, 'spacing' => -1.5, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',         'weight' => 400, 'lineHeight' => 1.4,  'min' => 28, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',         'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Inter',         'weight' => 700, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 2.5,  'case' => 'upper'],
                'stat'     => ['font' => 'Archivo Black', 'weight' => 400, 'lineHeight' => 0.96, 'min' => 80, 'max' => 150, 'spacing' => -1.5, 'case' => 'none'],
                'caption'  => ['font' => 'Inter',         'weight' => 600, 'lineHeight' => 1.1,  'min' => 32, 'max' => 54,  'spacing' => 0.0,  'case' => 'none'],
            ],
        ],
        // Garamond-class old-style serif + grounded sans. Quiet luxury / editorial.
        'elegant-garamond' => [
            'name' => 'Elegant Garamond', 'personality' => 'classic, literary, understated',
            'text' => [
                'headline' => ['font' => 'Cormorant Garamond', 'weight' => 600, 'lineHeight' => 1.0, 'min' => 64, 'max' => 180, 'spacing' => -0.5, 'case' => 'none'],
                'subhead'  => ['font' => 'Work Sans',          'weight' => 400, 'lineHeight' => 1.4, 'min' => 30, 'max' => 52,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Work Sans',          'weight' => 400, 'lineHeight' => 1.5, 'min' => 28, 'max' => 46,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'Work Sans',          'weight' => 600, 'lineHeight' => 1.0, 'min' => 24, 'max' => 36,  'spacing' => 2.5,  'case' => 'upper'],
                'stat'     => ['font' => 'Cormorant Garamond', 'weight' => 700, 'lineHeight' => 1.0, 'min' => 90, 'max' => 200, 'spacing' => -0.5, 'case' => 'none'],
                'caption'  => ['font' => 'Work Sans',          'weight' => 500, 'lineHeight' => 1.3, 'min' => 32, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
            ],
        ],
        // Grotesk display + mono eyebrow (numbered kicker). Dev tools / data / crypto.
        'tech-mono' => [
            'name' => 'Tech Mono', 'personality' => 'precise, technical, structured',
            'text' => [
                'headline' => ['font' => 'Space Grotesk',  'weight' => 700, 'lineHeight' => 0.98, 'min' => 58, 'max' => 124, 'spacing' => -1.5, 'case' => 'none'],
                'subhead'  => ['font' => 'Inter',           'weight' => 400, 'lineHeight' => 1.4,  'min' => 28, 'max' => 50,  'spacing' => 0.0,  'case' => 'none'],
                'body'     => ['font' => 'Inter',           'weight' => 400, 'lineHeight' => 1.5,  'min' => 26, 'max' => 44,  'spacing' => 0.0,  'case' => 'none'],
                'eyebrow'  => ['font' => 'JetBrains Mono',  'weight' => 500, 'lineHeight' => 1.0,  'min' => 24, 'max' => 36,  'spacing' => 1.5,  'case' => 'upper'],
                'stat'     => ['font' => 'Space Grotesk',   'weight' => 700, 'lineHeight' => 0.96, 'min' => 80, 'max' => 150, 'spacing' => -1.5, 'case' => 'none'],
                'caption'  => ['font' => 'JetBrains Mono',  'weight' => 500, 'lineHeight' => 1.2,  'min' => 30, 'max' => 48,  'spacing' => 0.5,  'case' => 'none'],
            ],
        ],
    ];

    /** @return string[] */
    public static function ids(): array
    {
        return array_keys(self::PAIRINGS);
    }

    public static function has(string $id): bool
    {
        return isset(self::PAIRINGS[$id]);
    }

    /** @return array<string, mixed>|null Raw entry (name/personality/text). */
    public static function meta(string $id): ?array
    {
        return self::PAIRINGS[$id] ?? null;
    }

    /**
     * The `text` typography map for a pairing (Styles-shaped). Falls back to the
     * first pairing for an unknown id.
     *
     * @return array<string, array<string, mixed>>
     */
    public static function text(string $id): array
    {
        $entry = self::PAIRINGS[$id] ?? self::PAIRINGS[array_key_first(self::PAIRINGS)];

        return $entry['text'];
    }

    /** Every distinct family used across all pairings (for the preview loader). @return string[] */
    public static function families(): array
    {
        $out = [];
        foreach (self::PAIRINGS as $p) {
            foreach ($p['text'] as $t) {
                $out[$t['font']] = true;
            }
        }

        return array_keys($out);
    }
}
