<?php

namespace App\Services;

use App\Core\Config;
use App\Helpers\Logger;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class OpenAIService
{
    public const DEFAULT_ARTICLE_PROMPT_TEMPLATE = 'Jestes doswiadczonym redaktorem SEO i ghostwriterem. Pisz po polsku naturalnie, konkretnie i bez AI-owych klisz. Artykul ma miec od {min_words} do {max_words} slow. Formatuj tresc tylko jako HTML (bez <html>, <body>, <head>) i zachowaj strukture: lead, sekcje H2/H3, listy praktyczne, sekcja FAQ. Uzywaj realnych przykladow i jezyka, ktory brzmi jak tekst napisany przez czlowieka z doswiadczeniem. Nie naduzywaj pytan retorycznych i unikaj fraz typu: "w dzisiejszych czasach", "podsumowujac", "warto zauwazyc". Zwracaj odpowiedz WYLACZNIE jako JSON: {"title":"...","content":"..."}';

    private const MIN_ACCEPTABLE_H2 = 3;

    private Client $client;

    public function __construct()
    {
        $this->client = new Client([
            'base_uri' => 'https://api.openai.com/v1/',
            'timeout' => 120,
        ]);
    }

    public function generateArticle(string $topicName, string $topicDescription, array $existingTitles): ?array
    {
        $apiKey = Config::getDbSetting('openai_api_key', Config::get('OPENAI_API_KEY'));
        $model = Config::getDbSetting('openai_model', Config::get('OPENAI_MODEL', 'gpt-4o'));
        $minWords = $this->sanitizeWordLimit(Config::getDbSetting('article_min_words', '900'), 900);
        $maxWords = $this->sanitizeWordLimit(Config::getDbSetting('article_max_words', '1400'), 1400);

        if ($maxWords < $minWords) {
            $maxWords = $minWords;
        }

        $systemPromptTemplate = Config::getDbSetting('article_generation_prompt', self::DEFAULT_ARTICLE_PROMPT_TEMPLATE);

        if (!is_string($systemPromptTemplate) || trim($systemPromptTemplate) === '') {
            $systemPromptTemplate = self::DEFAULT_ARTICLE_PROMPT_TEMPLATE;
        }

        if (empty($apiKey)) {
            Logger::error('OpenAI API key not configured', 'openai');
            return null;
        }

        $existingList = !empty($existingTitles)
            ? implode("\n- ", $existingTitles)
            : '(brak - to pierwszy artykul z tego tematu)';

        $systemPrompt = strtr($systemPromptTemplate, [
            '{min_words}' => (string) $minWords,
            '{max_words}' => (string) $maxWords,
        ]);

        $qualityFeedback = '';
        $lastPrompt = '';

        for ($attempt = 1; $attempt <= 3; $attempt++) {
            $userPrompt = $this->buildUserPrompt(
                $topicName,
                $topicDescription,
                $existingList,
                $qualityFeedback,
                $minWords,
                $maxWords
            );

            $fullPrompt = $systemPrompt . "\n\n" . $userPrompt;
            $lastPrompt = $fullPrompt;

            $content = $this->requestStructuredArticle($apiKey, (string) $model, $systemPrompt, $userPrompt);
            if ($content === null) {
                continue;
            }

            $article = json_decode($content, true);
            if (!isset($article['title']) || !isset($article['content'])) {
                Logger::error('Invalid JSON structure from OpenAI: ' . $content, 'openai');
                $qualityFeedback = 'Zwroc poprawny JSON z polami "title" i "content".';
                continue;
            }

            $title = trim((string) ($article['title'] ?? ''));
            $htmlContent = trim((string) ($article['content'] ?? ''));
            $quality = $this->validateQuality($title, $htmlContent, $minWords);

            if (!$quality['ok']) {
                $qualityFeedback = 'Poprzednia wersja nie spelnila wymagan: ' . implode('; ', $quality['issues']) . '. Napisz nowa, lepsza wersje.';
                Logger::warning(
                    'Generated article quality retry for topic "' . $topicName . '" (attempt ' . $attempt . '): ' . implode('; ', $quality['issues']),
                    'openai'
                );
                continue;
            }

            Logger::info('Generated article: ' . $title, 'openai');

            return [
                'title' => $title,
                'content' => $htmlContent,
                'model' => (string) $model,
                'prompt' => $fullPrompt,
            ];
        }

        Logger::error('OpenAI generation failed after quality retries. Last feedback: ' . $qualityFeedback, 'openai');
        Logger::error('OpenAI generation failed after quality retries. Last feedback: ' . $qualityFeedback, 'publish');
        return null;
    }

    private function requestStructuredArticle(string $apiKey, string $model, string $systemPrompt, string $userPrompt): ?string
    {
        try {
            $response = $this->client->post('chat/completions', [
                'headers' => [
                    'Authorization' => "Bearer {$apiKey}",
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'model' => $model,
                    'messages' => [
                        ['role' => 'system', 'content' => $systemPrompt],
                        ['role' => 'user', 'content' => $userPrompt],
                    ],
                    'temperature' => 0.9,
                    'max_tokens' => 16000,
                    'response_format' => ['type' => 'json_object'],
                ],
            ]);
        } catch (GuzzleException $e) {
            Logger::error('OpenAI API error: ' . $e->getMessage(), 'openai');
            return null;
        }

        $data = json_decode($response->getBody()->getContents(), true);
        $content = $data['choices'][0]['message']['content'] ?? null;

        if (!is_string($content) || trim($content) === '') {
            Logger::error('Empty response from OpenAI', 'openai');
            return null;
        }

        return $content;
    }

    private function buildUserPrompt(
        string $topicName,
        string $topicDescription,
        string $existingList,
        string $qualityFeedback,
        int $minWords,
        int $maxWords
    ): string {
        $prompt = "Napisz artykul na temat: {$topicName}\n";
        $prompt .= "KRYTYCZNE WYMAGANIE DLUGOSCI: Artykul MUSI miec minimum {$minWords} slow. Docelowo {$minWords}-{$maxWords} slow. Artykuly ponizej {$minWords} slow beda ODRZUCONE.\n";
        $prompt .= "Aby osiagnac wymagana dlugosc:\n";
        $prompt .= "- Kazda sekcja H2 musi miec minimum 150-200 slow z konkretnymi przykladami, danymi i scenariuszami.\n";
        $prompt .= "- Uzyj minimum 5 sekcji H2 (nie liczac FAQ i zakonczenia).\n";
        $prompt .= "- Rozwin kazdy punkt — nie pisz ogolnikow, podaj detale, porownania, liczby.\n\n";
        $prompt .= "Tytul ma byc samodzielny i nie moze zaczynac sie od nazwy tematu ani kategorii.\n";
        $prompt .= "Tresc ma byc konkretna, praktyczna i naturalna. Bez ogolnikow.\n";
        $prompt .= "Wstep: 2-3 krotkie akapity i jasna obietnica, czego czytelnik sie dowie.\n";
        $prompt .= "Srodek: minimum 5 sekcji H2, w kazdej przynajmniej jeden konkret (przyklad, liczba, scenariusz, checklista).\n";
        $prompt .= "Wstaw jedna sekcje H2 o nazwie \"Najczestsze bledy\".\n";
        $prompt .= "Opcjonalnie dodaj sekcje H2 \"FAQ\" z 3 pytaniami i odpowiedziami — tylko jesli pasuje do tematu.\n";
        $prompt .= "Zakonczenie ma byc praktyczne: \"Co warto zapamietac\" jako lista punktowana.\n";
        $prompt .= "Uzywaj tylko HTML: <p>, <h2>, <h3>, <ul>, <ol>, <li>, <strong>, <em>, <blockquote>, <table>, <tr>, <th>, <td>.\n";

        if ($topicDescription !== '') {
            $prompt .= "Wytyczne redakcyjne od klienta: {$topicDescription}\n";
        }

        $prompt .= "\nNie powielaj tematow, bo te tytuly juz istnieja:\n- {$existingList}\n";

        if ($qualityFeedback !== '') {
            $prompt .= "\nKrytyczne poprawki do wdrozenia: {$qualityFeedback}\n";
        }

        return $prompt;
    }

    private function validateQuality(string $title, string $content, int $minWords): array
    {
        $issues = [];
        $wordCount = $this->countWords($content);

        if ($title === '') {
            $issues[] = 'brak tytulu';
        }

        if ($content === '') {
            $issues[] = 'brak tresci';
        }

        $acceptableMin = (int) round($minWords * 0.6);
        if ($wordCount < $acceptableMin) {
            $issues[] = 'za malo slow (' . $wordCount . ', wymagane min ' . $acceptableMin . ')';
        }

        $h2Count = preg_match_all('/<h2\b[^>]*>/i', $content);
        if ($h2Count < self::MIN_ACCEPTABLE_H2) {
            $issues[] = 'za malo naglowkow H2';
        }

        $normalizedContent = $this->stripDiacritics(mb_strtolower($content));
        if (!str_contains($normalizedContent, 'co warto zapamietac')) {
            $issues[] = 'brak sekcji koncowej z konkretami';
        }

        return [
            'ok' => empty($issues),
            'issues' => $issues,
        ];
    }

    private function countWords(string $html): int
    {
        $plain = trim(strip_tags($html));
        if ($plain === '') {
            return 0;
        }

        $parts = preg_split('/\s+/u', $plain);
        if (!is_array($parts)) {
            return 0;
        }

        return count(array_filter($parts, static fn ($item) => $item !== ''));
    }

    private function stripDiacritics(string $text): string
    {
        $map = [
            'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l',
            'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ź' => 'z', 'ż' => 'z',
            'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'E', 'Ł' => 'L',
            'Ń' => 'N', 'Ó' => 'O', 'Ś' => 'S', 'Ź' => 'Z', 'Ż' => 'Z',
        ];

        return strtr($text, $map);
    }

    private function sanitizeWordLimit(mixed $value, int $default): int
    {
        $intValue = (int) $value;

        if ($intValue === 0) {
            return $default;
        }

        if ($intValue < 400) {
            return 400;
        }

        if ($intValue > 4000) {
            return 4000;
        }

        return $intValue;
    }
}
