<?php

namespace App\Services\AI;

use App\Contracts\ScriptGenerator;
use App\DTOs\CreativeBrief;
use App\Exceptions\VideoDialogueException;
use App\Support\VideoDialogue;
use Illuminate\Contracts\JsonSchema\JsonSchema;

use function Laravel\Ai\agent;

class LaravelAiScriptGenerator implements ScriptGenerator
{
    public function generate(CreativeBrief $brief, ?int $durationSeconds = null): array
    {
        return $this->generateOne($brief, durationSeconds: $durationSeconds);
    }

    public function generateVariations(CreativeBrief $brief, int $count = 4, ?int $durationSeconds = null): array
    {
        $variations = [];
        $lastDialogueException = null;

        foreach (range(1, $count) as $seed) {
            try {
                $variations[] = $this->generateOne($brief, variationSeed: $seed, durationSeconds: $durationSeconds);
            } catch (VideoDialogueException $exception) {
                $lastDialogueException = $exception;
            }
        }

        if ($variations === [] && $lastDialogueException !== null) {
            throw $lastDialogueException;
        }

        return $variations;
    }

    private function generateOne(CreativeBrief $brief, int $variationSeed = 1, ?int $durationSeconds = null): array
    {
        $angles = [
            1 => 'Angle 1 (Pain-Point & Rapid Solution): Open with an immediate, relatable daily frustration or problem, then position the product as the effortless solution.',
            2 => 'Angle 2 (Contrarian / Curiosity Pattern-Interrupt): Open with an unexpected question or bold contrarian statement that shatters assumptions and stops the scroll.',
            3 => 'Angle 3 (Benefit-First & Transformation): Open directly with the single most exciting payoff, confidence boost, or aesthetic result the user achieves.',
            4 => 'Angle 4 (Social Proof & Insider Discovery): Open from a peer discovery or trending recommendation perspective ("If you haven\'t discovered this yet...", "Why everyone is talking about...").',
        ];

        $angleDirective = $angles[$variationSeed] ?? "Angle variation #{$variationSeed}: Distinct creative angle with unique hook and presentation style.";

        // Same words/sec budget (config('creative.spoken_words_per_second')) the video providers
        // use for the rendered dialogue line — must stay in sync with GeminiVeoVideoAvatarProvider/
        // GeminiVeoVideoAvatarProvider's buildSpokenLine(), or the generated script will run
        // longer than what the video actually has room to speak.
        // Mirrors the ~1s end-of-clip safety buffer used by both video providers' buildSpokenLine(),
        // so the script the AI writes is never longer than what the rendered video actually has
        // room to speak in full before the clip ends.
        $durationLine = $durationSeconds
            ? sprintf("\n\nThis is a %d-second video — make spoken_line one complete natural sentence of at most %d words including the CTA. Do not concatenate the other fields to form the speech.", $durationSeconds, VideoDialogue::wordBudget($durationSeconds))
            : '';

        $brandLine = filled($brief->projectName) ? "Brand/Project: {$brief->projectName}\n" : '';

        $prompt = sprintf(
            "%sProduct: %s\nFeatures: %s\nBenefits: %s\nMessaging: %s\nCTA: %s\n\nTarget Marketing Angle: %s\n\nWrite video ad script variation #%d using the target angle above. Make the hook meaningfully different from all other variations. ".
            'The CTA is the primary conversion goal of this ad — use the CTA given above verbatim or near-verbatim, keep it short, unambiguous, and action-oriented, and never omit or water it down under time pressure.%s',
            $brandLine,
            $brief->productName,
            implode(', ', $brief->features),
            implode(', ', $brief->benefits),
            $brief->messaging,
            $brief->cta,
            $angleDirective,
            $variationSeed,
            $durationLine,
        );

        for ($attempt = 0; $attempt < 2; $attempt++) {
            $response = agent(
                instructions: 'Write a concise, natural video advertisement. The spoken_line is the exact dialogue: one coherent line with a hook, one verified benefit, and the CTA. Keep the spoken_line within the word budget in the prompt; never rely on truncation. The hook, problem, product, benefits, and cta fields are planning fields and must not be concatenated to form speech. Describe a simple visual_action for the presenter. Use only verified product facts and avoid real-person references, unsupported claims, and sensitive or violent language.',
                schema: fn (JsonSchema $schema) => [
                    'hook' => $schema->string()->required(),
                    'spoken_line' => $schema->string()->required(),
                    'visual_action' => $schema->string()->required(),
                    'problem' => $schema->string()->required(),
                    'product' => $schema->string()->required(),
                    'benefits' => $schema->string()->required(),
                    'cta' => $schema->string()->required(),
                ],
            )->prompt($prompt, provider: TextProviderFailover::providers());

            $script = $response->toArray();

            if (! filled($script['spoken_line'] ?? null)) {
                $script['spoken_line'] = trim(implode(' ', array_filter([
                    $script['hook'] ?? null,
                    $script['cta'] ?? null,
                ])));
            }

            if ($durationSeconds === null) {
                return $script;
            }

            try {
                VideoDialogue::assertFits((string) $script['spoken_line'], $durationSeconds);

                return $script;
            } catch (VideoDialogueException $exception) {
                if ($attempt === 1) {
                    throw $exception;
                }

                $prompt .= sprintf(
                    "\n\nThe previous spoken_line contained %d words. Rewrite it as a complete line of at most %d words, preserving a clear call to action.",
                    VideoDialogue::wordCount((string) $script['spoken_line']),
                    VideoDialogue::wordBudget($durationSeconds),
                );
            }
        }

        throw new VideoDialogueException('Could not generate dialogue for the selected duration. Please try again.');
    }
}
