<?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, ?string $engine = null): array
    {
        return $this->generateOne($brief, durationSeconds: $durationSeconds, engine: $engine);
    }

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

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

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

        return $variations;
    }

    private function generateOne(CreativeBrief $brief, int $variationSeed = 1, ?int $durationSeconds = null, ?string $engine = 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.";

        $durationLine = '';
        if ($durationSeconds) {
            $floor = VideoDialogue::wordFloor($durationSeconds, $engine);
            $budget = VideoDialogue::wordBudget($durationSeconds, $engine);
            $durationLine = $engine === HiggsfieldSeedanceVideoAvatarProvider::ENGINE
                ? sprintf(
                    "\n\nThis is a %d-second video. Write spoken_line as one complete natural sentence of %d to %d words, including the CTA, so the speech lasts until one second before the video ends. The last second is a silent product shot with no extra words. Do not concatenate the other fields to form the speech.",
                    $durationSeconds,
                    $floor,
                    $budget,
                )
                : sprintf(
                    "\n\nThis is a %d-second video. Write spoken_line as one complete natural sentence of %d to %d words, including the CTA, so the speech fills the clip and ends as the video ends — not several seconds early, and not after the clip is over. Do not concatenate the other fields to form the speech.",
                    $durationSeconds,
                    $floor,
                    $budget,
                );
        }

        $brandLine = filled($brief->projectName) ? "Brand/Project: {$brief->projectName}\n" : '';
        $handlingLine = match ($brief->handling) {
            'display' => "\n\nThe product is heavy and stays in place. visual_action must show the presenter beside it, gesturing or pointing. Never holding, lifting, tossing, or carrying it.",
            'handheld' => "\n\nThe product is light enough to hold. visual_action may show the presenter holding it up and pointing to a detail.",
            default => '',
        };

        $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%s',
            $brandLine,
            $brief->productName,
            implode(', ', $brief->features),
            implode(', ', $brief->benefits),
            $brief->messaging,
            $brief->cta,
            $angleDirective,
            $variationSeed,
            $durationLine,
            $handlingLine,
        );

        for ($attempt = 0; $attempt < 2; $attempt++) {
            $response = agent(
                instructions: 'Write a natural video advertisement. The spoken_line is the exact dialogue: one coherent line with a hook, one verified benefit, and the CTA. It must use enough words to last the full video duration and must stay inside the word range in the prompt; never rely on truncation and do not finish the speech early. 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;
            }

            $wordCount = VideoDialogue::wordCount((string) $script['spoken_line']);
            $budget = VideoDialogue::wordBudget($durationSeconds, $engine);
            $floor = VideoDialogue::wordFloor($durationSeconds, $engine);

            if ($wordCount >= $floor && $wordCount <= $budget) {
                return $script;
            }

            if ($attempt === 1) {
                VideoDialogue::assertFits((string) $script['spoken_line'], $durationSeconds);

                throw new VideoDialogueException('Video dialogue is too short for the selected duration. Please generate a longer script.');
            }

            $prompt .= sprintf(
                "\n\nThe previous spoken_line contained %d words, which is %s for a %d-second video. Rewrite it as a complete line of %d to %d words so the speech fills the clip and ends with it, preserving a clear call to action.",
                $wordCount,
                $wordCount > $budget ? 'too long' : 'too short',
                $durationSeconds,
                $floor,
                $budget,
            );
        }

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