<?php

declare(strict_types=1);

namespace App\Services\AiBiography;

/**
 * Builds the LLM prompt payload from a normalized creator input.
 *
 * v1.1 changes:
 *   – PROMPT_VERSION constant tracks the active template family.
 *   – Improved system prompt discourages formulaic openings and stat-dumps.
 *   – Sparse profile branch uses a lighter, safer template.
 *   – Strict mode is used on retry; produces a more conservative output.
 *
 * Prompt rules:
 *   – Only include facts that are actually present in the input.
 *   – Never instruct the model to invent details or speculate.
 *   – Always require a single paragraph output with no markdown.
 *   – Keep max_tokens tight to enforce the word cap.
 */
final class AiBiographyPromptBuilder
{
    public const PROMPT_VERSION = 'v1.1';
    private const MIN_WORDS = 30;

    private const SYSTEM_PROMPT = <<<'PROMPT'
You are a concise writing assistant for Skinbase, a digital art platform.

Write short creator biographies using only the facts provided. Use a polished, factual, and slightly editorial tone.

Rules:
- Use only the provided data. Do not invent achievements, personal details, visual style claims, or platform fame.
- Do not write bullet points, headings, or markdown.
- Output exactly one paragraph.
- Do not exceed 140 words.
- Avoid hype language: do not use "world-class", "iconic", "legendary", "renowned", "celebrated", "masterpiece", or "beloved".
- Do not speculate about personality, age, gender, politics, religion, or private life.
- Do not mention data points that are not provided or are zero/empty.
- Do not open with "has been part of Skinbase since" or similar formulaic phrases. Vary the opening.
- Mention only the 2 to 3 most meaningful signals. Do not list every available stat.
- Do not write "creator journey shows..." — describe what the data reflects directly.
- Prefer natural narrative flow over data listing.
PROMPT;

    private const SYSTEM_PROMPT_STRICT = <<<'PROMPT'
You are a cautious writing assistant for Skinbase, a digital art platform.

Write a short, safe creator biography using only the facts provided. Be conservative.

Rules:
- Use only the provided facts. Do not invent or speculate.
- Output exactly one paragraph, no markdown, no headings, no bullets.
- Maximum 100 words.
- Mention only 1 or 2 standout facts. Do not list all available data.
- Avoid any superlatives, praise, or style claims.
- Do not mention missing or zero-value fields.
- Keep the tone neutral, simple, and factual.
PROMPT;

    private const SYSTEM_PROMPT_SPARSE = <<<'PROMPT'
You are a cautious writing assistant for Skinbase, a digital art platform.

Write a short, modest creator introduction using only the facts provided.

Rules:
- Use only the facts provided.
- Output exactly one paragraph, no markdown, no bullets.
- Write between 35 and 60 words.
- Minimum 30 words.
- Keep it simple. Mention member-since year and upload count if available.
- Add one category or another factual signal when available so the paragraph has enough substance.
- Do not invent anything. Do not praise. Do not speculate.
- If data is very limited, use two short factual sentences rather than a fragment.
PROMPT;

    private const SYSTEM_PROMPT_SPARSE_STRICT = <<<'PROMPT'
You are a cautious writing assistant for Skinbase, a digital art platform.

Write a short, modest creator introduction using only the facts provided. Be conservative and precise.

Rules:
- Use only the facts provided.
- Output exactly one paragraph, no markdown, no bullets.
- Write between 35 and 50 words.
- Minimum 30 words.
- Prefer two short factual sentences.
- Mention member-since year, upload count, and one category when available.
- Do not invent anything. Do not praise. Do not speculate.
PROMPT;

    /**
     * Build the full messages payload for the LLM.
     *
     * @param  array<string, mixed>  $input   normalized creator input from AiBiographyInputBuilder
     * @param  bool                  $strict  true on retry — forces more conservative output
     * @param  bool                  $sparse  true for sparse-profile creators
     * @return array{messages: list<array{role: string, content: string}>, max_tokens: int, temperature: float, stream: bool, prompt_version: string}
     */
    public function build(array $input, bool $strict = false, bool $sparse = false): array
    {
        if ($sparse && $strict) {
            $systemPrompt = self::SYSTEM_PROMPT_SPARSE_STRICT;
            $userPrompt   = $this->buildSparseUserPrompt($input, strict: true);
            $maxTokens    = 240;
            $temperature  = 0.2;
        } elseif ($sparse) {
            $systemPrompt = self::SYSTEM_PROMPT_SPARSE;
            $userPrompt   = $this->buildSparseUserPrompt($input, strict: false);
            $maxTokens    = 320;
            $temperature  = 0.3;
        } elseif ($strict) {
            $systemPrompt = self::SYSTEM_PROMPT_STRICT;
            $userPrompt   = $this->buildUserPrompt($input, strict: true);
            $maxTokens    = 450;
            $temperature  = 0.25;
        } else {
            $systemPrompt = self::SYSTEM_PROMPT;
            $userPrompt   = $this->buildUserPrompt($input, strict: false);
            $maxTokens    = 600;
            $temperature  = 0.45;
        }

        return [
            'messages'       => [
                ['role' => 'system', 'content' => $systemPrompt],
                ['role' => 'user',   'content' => $userPrompt],
            ],
            'max_tokens'     => $maxTokens,
            'temperature'    => $temperature,
            'stream'         => false,
            'prompt_version' => self::PROMPT_VERSION,
        ];
    }

    private function buildUserPrompt(array $input, bool $strict): string
    {
        $wordTarget = $strict ? '60 to 100' : '70 to 130';

        $lines = [
            "Write a creator biography in {$wordTarget} words using only the facts below. Output one paragraph only.",
            '',
        ];

        $username = (string) ($input['username'] ?? '');
        if ($username !== '') {
            $lines[] = "- Creator: {$username}";
        }

        $memberYear = $input['member_since_year'] ?? null;
        $years      = $input['years_on_skinbase'] ?? null;
        if ($memberYear !== null && (int) $memberYear > 0) {
            $label = ((int) ($years ?? 0) > 1) ? ", {$years} years on the platform" : '';
            $lines[] = "- Member since: {$memberYear}{$label}";
        }

        $uploads = $input['uploads_count'] ?? 0;
        if ((int) $uploads > 0) {
            $lines[] = "- Total public uploads: {$uploads}";
        }

        $featured = $input['featured_count'] ?? 0;
        if ((int) $featured > 0) {
            $lines[] = "- Featured artworks: {$featured}";
        }

        $downloads = $input['downloads_count'] ?? 0;
        if ((int) $downloads > 5000) {
            $lines[] = sprintf('- Total downloads: %s', number_format((int) $downloads));
        }

        $categories = $input['top_categories'] ?? [];
        if ($categories !== []) {
            $lines[] = '- Top categories: ' . implode(', ', array_slice((array) $categories, 0, 2));
        }

        // On strict retry, trim tags to keep prompt tight and reduce hallucination surface.
        if (! $strict) {
            $tags = $input['top_tags'] ?? [];
            if ($tags !== []) {
                $lines[] = '- Common themes: ' . implode(', ', array_slice((array) $tags, 0, 3));
            }
        }

        $bestWork = $input['best_performing_work'] ?? null;
        if (is_array($bestWork) && isset($bestWork['title'], $bestWork['year'])) {
            $lines[] = "- Best-performing work: {$bestWork['title']} ({$bestWork['year']})";
        }

        $productiveYear = $input['most_productive_year'] ?? null;
        if ($productiveYear !== null && (int) $productiveYear > 0) {
            $lines[] = "- Most productive year: {$productiveYear}";
        }

        $status = $input['current_activity_status'] ?? null;
        if ($status !== null && $status !== '') {
            $statusLabels = [
                'active'          => 'currently active',
                'recently_active' => 'recently active',
                'returning'       => 'returning creator',
                'legacy'          => 'long-standing creator',
                'inactive'        => null,
            ];
            $statusLabel = $statusLabels[$status] ?? null;
            if ($statusLabel !== null) {
                $lines[] = "- Activity: {$statusLabel}";
            }
        }

        $milestones = $input['milestones'] ?? [];
        if (is_array($milestones)) {
            if (! empty($milestones['has_comeback'])) {
                $lines[] = '- Notable milestone: returned after a significant break';
            }
            $streak = (int) ($milestones['best_upload_streak_months'] ?? 0);
            if ($streak >= 3 && ! $strict) {
                $lines[] = "- Upload streak: {$streak} consecutive months";
            }
        }

        // Include eras and evolution only when not on strict retry.
        if (! $strict) {
            $eras = $input['eras'] ?? [];
            if (is_array($eras) && count($eras) >= 2) {
                $eraTitles = array_column($eras, 'title');
                $lines[] = '- Creator eras: ' . implode(' → ', $eraTitles);
            }

            $evolutionCount = $input['evolution_count'] ?? 0;
            if ((int) $evolutionCount > 0) {
                $lines[] = "- Remastered/evolved works: {$evolutionCount}";
            }
        }

        $lines[] = '';
        $lines[] = 'Avoid hype. Do not open with a formulaic phrase. Do not list every stat. Output one paragraph only. No markdown.';

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

    private function buildSparseUserPrompt(array $input, bool $strict = false): string
    {
        $wordTarget = $strict ? '35 to 50' : '35 to 60';
        $lines = [
            "Write a brief, modest creator introduction in {$wordTarget} words using only these facts. Output one paragraph only.",
            '',
        ];

        $username = (string) ($input['username'] ?? '');
        if ($username !== '') {
            $lines[] = "- Creator: {$username}";
        }

        $memberYear = $input['member_since_year'] ?? null;
        $years      = $input['years_on_skinbase'] ?? null;
        if ($memberYear !== null && (int) $memberYear > 0) {
            $yearsLabel = ((int) ($years ?? 0) > 1) ? ", {$years} years on the platform" : '';
            $lines[] = "- Member since: {$memberYear}{$yearsLabel}";
        }

        $uploads = $input['uploads_count'] ?? 0;
        if ((int) $uploads > 0) {
            $lines[] = "- Public uploads: {$uploads}";
        }

        $categories = $input['top_categories'] ?? [];
        if ($categories !== []) {
            $lines[] = '- Categories: ' . implode(', ', array_slice((array) $categories, 0, $strict ? 1 : 2));
        }

        $lines[] = '';
        $lines[] = 'Keep it simple and factual. Write at least ' . self::MIN_WORDS . ' words. Prefer two short sentences if needed. No praise. No markdown.';

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