<?php

namespace App\Services\AI;

use App\Contracts\PromptLandingPageBriefResolver;
use App\DTOs\PromptLandingPageBrief;
use App\DTOs\WebsiteBrandSignals;
use App\Models\PromptLandingPageAttachment;
use App\Support\CssFontFamily;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Log;
use Laravel\Ai\Files\LocalDocument;
use Laravel\Ai\Files\LocalImage;

use function Laravel\Ai\agent;

class LaravelAiPromptLandingPageBriefResolver implements PromptLandingPageBriefResolver
{
    public function __construct(
        private WebsiteBrandSignalExtractor $brandScanner,
    ) {}

    /**
     * @param  list<PromptLandingPageAttachment>  $attachments
     */
    public function resolve(
        string $prompt,
        array $attachments = [],
        ?string $websiteUrl = null,
        ?string $purpose = null,
    ): PromptLandingPageBrief {
        $prompt = trim($prompt);
        $websiteUrl = trim((string) $websiteUrl);
        $purpose = trim((string) $purpose);
        $inlineText = $this->inlineTextDocuments($attachments);

        if ($prompt === '' && $inlineText === '' && $attachments === []) {
            $prompt = 'Create a conversion-focused landing page from the information available.';
        }

        $parts = [];

        if ($purpose !== '') {
            $parts[] = "Landing page purpose:\n{$purpose}";
        }

        if ($websiteUrl !== '') {
            $parts[] = "Brand website to match:\n{$websiteUrl}";
        }

        if ($prompt !== '') {
            $parts[] = "User prompt:\n{$prompt}";
        }

        if ($inlineText !== '') {
            $parts[] = "Text extracted from attached documents:\n{$inlineText}";
        }

        if ($attachments !== []) {
            $names = collect($attachments)
                ->map(fn (PromptLandingPageAttachment $file) => $file->original_name)
                ->implode(', ');
            $parts[] = "Attached files (analyze only; never put these files on the landing page): {$names}";
        }

        $brandSignals = null;
        $scanUrl = $websiteUrl !== '' ? $websiteUrl : ($this->brandScanner->extractUrls($prompt)[0] ?? null);

        if ($scanUrl !== null) {
            $brandSignals = $this->brandScanner->scan($scanUrl);
        }

        if ($brandSignals !== null) {
            $parts[] = $brandSignals->summaryText;

            if ($brandSignals->hasHeaderColor()) {
                $parts[] = "Measured header CTA color from the live rendered page: {$brandSignals->headerColor}. This is accent_color — the theme for buttons, links, header highlights, and CTAs. Do not substitute Bootstrap or stylesheet defaults such as #0d6efd or #198754.";
            }

            if ($brandSignals->hasHeaderBackgroundColor()) {
                $parts[] = "Measured header/page background from the live rendered page: {$brandSignals->headerBackgroundColor}. Use this as background_color. Do not invent a dark theme when this background is light.";
            }

            if ($brandSignals->hasExactLogoSvg()) {
                $parts[] = "EXACT logo SVG extracted from the live, rendered site. Set logo_present to true, set logo_type to the best match, and copy this markup byte-for-byte into logo_exact_svg (do not alter, simplify, or re-describe it). Leave logo_description as a short plain-language label only, since the exact markup is authoritative:\n".$brandSignals->exactLogoSvg;
            } elseif ($brandSignals->hasLogoPublicUrl()) {
                $logoIsFavicon = $brandSignals->faviconUrl !== null
                    && $brandSignals->logoPublicUrl === $brandSignals->faviconUrl;

                $parts[] = $logoIsFavicon
                    ? "No header logo was found. Use this site favicon as the header <img> src (it is a public website asset, not an upload): {$brandSignals->logoPublicUrl}"
                    : "Public header logo URL from the live site. Set logo_present to true. The generated page may use this as an <img> src (it is a public website asset, not an upload): {$brandSignals->logoPublicUrl}";
            }

            if ($brandSignals->hasFaviconUrl()) {
                $parts[] = "Site favicon URL from the live site. The app injects this as <link rel=\"icon\">; keep logo_present true if this is also the header mark: {$brandSignals->faviconUrl}";
            }

            if ($brandSignals->hasFontFamily()) {
                $parts[] = "Measured body font-family from the live rendered page: {$brandSignals->fontFamily}. The app will use this typeface unless the user named a different font in the prompt.";
            }
        }

        $multimodalAttachments = $this->multimodalAttachments($attachments);

        if ($brandSignals?->hasLogoImage()) {
            $multimodalAttachments[] = new LocalImage($brandSignals->logoImagePath, $brandSignals->logoImageMime);
        }

        $response = agent(
            // instructions: 'You are an expert marketing analyst. Turn the user\'s prompt and any attached documents or images into a structured landing-page brief. Infer reasonable brand, offer, and CTA details from what is given. Do not invent product URLs. Do not treat attached files as assets to embed on the page — extract facts, copy, positioning, and visual brand styling only (never the files themselves). If attachments include screenshots, mockups, a PDF of an existing site, or a logo, identify the actual hex colors used for backgrounds, primary/accent elements, and text, as precisely as you can tell from the image. Also look for a logo or wordmark in any attachment: describe its structure (wordmark, icon, or combination mark), the exact brand text if it includes one, and enough visual detail — shape, geometry, letterforms, weight, colors used — that it could be faithfully redrawn as a simple inline SVG or styled text lockup. Do not describe or reference the uploaded file itself, only what the logo looks like. If no logo is visible, set logo_present to false and leave the other logo fields empty. If no visual reference is given for colors, infer a sensible palette from the described tone/industry instead of leaving colors blank.',
            instructions: 'You are an expert marketing analyst. Turn the user\'s prompt, the stated landing-page purpose, any attached documents or images, and any analyzed website signals into a structured landing-page brief. Infer reasonable brand, offer, and CTA details from what is given. Shape the page around the stated purpose. Do not invent product URLs. Do not treat attached files as things to embed on the new page — extract facts, copy, positioning, and visual brand styling only. If a website was analyzed, treat the measured header CTA color as accent_color (the theme for buttons and CTAs) and the measured header/page background as background_color. Ignore generic framework stylesheet colors such as Bootstrap #0d6efd or #198754. Do not invent a dark interface when the scanned background is light. The app applies the measured website font-family unless the user named a different font in the prompt. If an EXACT logo SVG was provided, copy it verbatim into logo_exact_svg with zero changes and keep logo_description brief. If a public header logo URL was provided, set logo_present to true and describe the logo briefly — do not set logo_present to false. If no exact SVG was provided but a logo image or screenshot is available, describe it precisely enough — shape, geometry, letterforms, weight, colors — that it could be faithfully redrawn as inline SVG, and leave logo_exact_svg empty. If no logo is visible anywhere, set logo_present to false and leave all logo fields empty. If no visual reference is given for colors, infer a sensible palette from the described tone/industry instead of leaving colors blank.',
            schema: fn (JsonSchema $schema) => [
                'brand_name' => $schema->string()->description('Brand or business name.')->required(),
                'offering' => $schema->string()->description('Product, service, or offer being promoted.')->required(),
                'headline' => $schema->string()->description('A conversion-focused hero headline.')->required(),
                'features' => $schema->array()->items($schema->string())->description('Key features.')->required(),
                'benefits' => $schema->array()->items($schema->string())->description('Key customer benefits.')->required(),
                'pricing' => $schema->string()->description('Pricing or offer details, or unknown.')->required(),
                'audience' => $schema->string()->description('Target audience.')->required(),
                'usps' => $schema->array()->items($schema->string())->description('Unique selling points.')->required(),
                'pain_points' => $schema->array()->items($schema->string())->description('Customer pain points.')->required(),
                'tone' => $schema->string()->description('Voice and visual tone.')->required(),
                'cta' => $schema->string()->description('Primary call to action.')->required(),
                'primary_color' => $schema->string()->description('Hex code of the dominant brand color, taken from attachments/website if visible, otherwise inferred from tone.')->required(),
                'secondary_color' => $schema->string()->description('Hex code of a supporting brand color.')->required(),
                'accent_color' => $schema->string()->description('Hex code used for CTAs/highlights.')->required(),
                'background_color' => $schema->string()->description('Hex code of the page/section background.')->required(),
                'text_color' => $schema->string()->description('Hex code for primary body text, chosen for contrast against background_color.')->required(),
                'logo_present' => $schema->boolean()->description('Whether a logo or wordmark is visible in any attachment or scanned website.')->required(),
                'logo_type' => $schema->string()->description('One of: wordmark, icon, combination-mark, unknown.')->required(),
                'logo_wordmark_text' => $schema->string()->description('The exact brand text shown in the logo, if any. Empty string if icon-only or no logo.')->required(),
                'logo_description' => $schema->string()->description('A short plain-language description of the logo. If logo_exact_svg is set, keep this brief since the SVG is authoritative. Otherwise, a precise visual description sufficient to redraw it as inline SVG.')->required(),
                'logo_exact_svg' => $schema->string()->description('Exact SVG markup copied verbatim from a scanned website, when provided. Empty string if no exact SVG was given.')->required(),
                'notes' => $schema->string()->description('Other constraints from the prompt or attachments.')->required(),
            ],
        )->prompt(
            implode("\n\n", $parts),
            attachments: $multimodalAttachments,
            provider: TextProviderFailover::providers(),
            timeout: 1200
        );

        $payload = $response->toArray();
        Log::info('Prompt landing page brief AI response', [
            'attachments' => collect($attachments)->pluck('original_name')->all(),
            'scanned_url' => $brandSignals?->url,
            'used_header_color' => $brandSignals?->headerColor,
            'used_exact_logo_svg' => $brandSignals?->hasExactLogoSvg() ?? false,
            'used_logo_url' => $brandSignals?->logoPublicUrl,
            'used_favicon_url' => $brandSignals?->faviconUrl,
            'json' => $payload,
        ]);

        if ($brandSignals?->hasLogoImage()) {
            @unlink($brandSignals->logoImagePath);
        }

        $payload['purpose'] = $purpose;

        $brief = PromptLandingPageBrief::fromArray($payload);
        $promptFont = CssFontFamily::fromUserPrompt($prompt);

        if ($brandSignals !== null) {
            return $this->applyScannedBrand($brief, $brandSignals, $promptFont);
        }

        if ($promptFont !== null) {
            $data = $brief->toArray();
            $data['font_family'] = $promptFont;
            $data['font_stylesheet_url'] = CssFontFamily::stylesheetUrl($promptFont) ?? '';

            return PromptLandingPageBrief::fromArray($data);
        }

        return $brief;
    }

    private function applyScannedBrand(PromptLandingPageBrief $brief, WebsiteBrandSignals $signals, ?string $promptFont): PromptLandingPageBrief
    {
        $data = $brief->toArray();

        if ($signals->hasHeaderColor()) {
            $data['accent_color'] = $signals->headerColor;
        }

        if ($signals->hasHeaderBackgroundColor()) {
            $data['background_color'] = $signals->headerBackgroundColor;
            $data['text_color'] = $this->contrastingTextColor($signals->headerBackgroundColor);
        }

        if ($signals->hasExactLogoSvg()) {
            $data['logo_present'] = true;
            $data['logo_exact_svg'] = $signals->exactLogoSvg;
        }

        if ($signals->hasLogoPublicUrl()) {
            $data['logo_present'] = true;
            $data['logo_url'] = $signals->logoPublicUrl;
        }

        if ($signals->hasFaviconUrl()) {
            $data['favicon_url'] = $signals->faviconUrl;
        }

        if ($promptFont !== null) {
            $data['font_family'] = $promptFont;
            $data['font_stylesheet_url'] = CssFontFamily::stylesheetUrl($promptFont) ?? '';
        } elseif ($signals->hasFontFamily()) {
            $data['font_family'] = $signals->fontFamily;
            $data['font_stylesheet_url'] = $signals->hasFontStylesheetUrl()
                ? $signals->fontStylesheetUrl
                : (CssFontFamily::stylesheetUrl($signals->fontFamily) ?? '');
        }

        return PromptLandingPageBrief::fromArray($data);
    }

    private function contrastingTextColor(string $backgroundHex): string
    {
        $hex = ltrim($backgroundHex, '#');

        if (strlen($hex) === 3) {
            $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
        }

        if (strlen($hex) !== 6) {
            return '#111111';
        }

        $value = hexdec($hex);
        $red = ($value >> 16) & 255;
        $green = ($value >> 8) & 255;
        $blue = $value & 255;
        $luminance = (0.2126 * $red + 0.7152 * $green + 0.0722 * $blue) / 255;

        return $luminance > 0.55 ? '#111111' : '#f8fafc';
    }

    /**
     * @param  list<PromptLandingPageAttachment>  $attachments
     */
    private function inlineTextDocuments(array $attachments): string
    {
        $blocks = [];

        foreach ($attachments as $attachment) {
            if ($attachment->isImage()) {
                continue;
            }

            $mime = strtolower((string) $attachment->mime);
            $name = strtolower($attachment->original_name);

            if (! str_contains($mime, 'text') && ! str_ends_with($name, '.txt') && ! str_ends_with($name, '.md')) {
                continue;
            }

            $path = $attachment->absolutePath();

            if (! is_file($path)) {
                continue;
            }

            $contents = @file_get_contents($path);

            if ($contents === false || trim($contents) === '') {
                continue;
            }

            $blocks[] = $attachment->original_name.":\n".trim($contents);
        }

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

    /**
     * @param  list<PromptLandingPageAttachment>  $attachments
     * @return list<LocalDocument|LocalImage>
     */
    private function multimodalAttachments(array $attachments): array
    {
        $files = [];

        foreach ($attachments as $attachment) {
            $path = $attachment->absolutePath();

            if (! is_file($path)) {
                continue;
            }

            $files[] = $attachment->isImage()
                ? new LocalImage($path, $attachment->mime)
                : new LocalDocument($path, $attachment->mime);
        }

        return $files;
    }
}
