<?php
declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/AzureOpenAiGateway.php';

/**
 * Korrespond — drafts replies or new correspondence to Norwegian authorities
 * (NAV, Barnevernet, schools, Bufdir, kommune, Statsforvalter, Trygderetten).
 *
 * Three passes (the third is opt-in):
 *   Pass 1 — classify(): fact-extract + gap-check (Azure gpt-4o-mini)
 *           returns missing_facts[]; caller may emit clarify and stop
 *   Pass 2 — generate(): retrieve law → draft (Azure gpt-4o) → self-check → translate
 *   Pass 3 — refine():   retrieve law scoped to a user-picked jurisdiction
 *           (norwegian|echr|both), rewrite the existing draft with formally-styled
 *           citations, append a "Rettskilder" / "Legal authorities" block.
 *
 * Canonical draft is always Norwegian bokmål. User-language draft is a translation.
 */
final class DbnKorrespondAgent
{
    private const CLASSIFY_DEPLOYMENT  = 'gpt-4o-mini';
    private const DRAFT_DEPLOYMENT     = 'gpt-4o';
    private const SELFCHECK_DEPLOYMENT = 'gpt-4o-mini';
    private const MAX_CONTEXT_CHARS    = 24000;
    private const MAX_DRAFT_TOKENS     = 2000;

    /** Recipient-body presets → default slice toggles for law retrieval. */
    private const BODY_PRESETS = [
        'barnehage'      => ['family_core', 'bufdir_guidance'],
        'school_1_10'    => ['family_core', 'broader_legal', 'echr'],
        'sfo'            => ['family_core', 'bufdir_guidance'],
        'nav'            => ['broader_legal', 'family_core'],
        'bufdir'         => ['bufdir_guidance', 'family_core', 'echr'],
        'barnevernet'    => ['child_welfare', 'echr', 'bufdir_guidance', 'family_core'],
        'kommune_other'  => ['broader_legal', 'family_core'],
        'statsforvalter' => ['child_welfare', 'broader_legal', 'family_core'],
        'trygderetten'   => ['broader_legal', 'echr'],
        'tingrett'       => ['family_core', 'echr', 'norwegian_courts'],
        'other'          => ['broader_legal', 'family_core'],
    ];

    /** Human labels for the recipient body, in Norwegian (used in prompt). */
    private const BODY_LABELS = [
        'barnehage'      => 'barnehagen (kindergarten)',
        'school_1_10'    => 'skolen (grunnskolen 1.–10. trinn)',
        'sfo'            => 'SFO (skolefritidsordningen)',
        'nav'            => 'NAV',
        'bufdir'         => 'Bufdir (Barne-, ungdoms- og familiedirektoratet)',
        'barnevernet'    => 'barnevernstjenesten',
        'kommune_other'  => 'kommunen',
        'statsforvalter' => 'Statsforvalteren',
        'trygderetten'   => 'Trygderetten',
        'tingrett'       => 'Tingretten',
        'other'          => 'mottaker',
    ];

    private DbnAzureOpenAiGateway $azure;

    public function __construct(?DbnAzureOpenAiGateway $azure = null)
    {
        $this->azure = $azure ?: new DbnAzureOpenAiGateway();
    }

    /** Localized chrome/progress strings, keyed by user UI language. */
    public static function L(string $key, string $lang, array $vars = []): string
    {
        $strings = [
            'analyzing' => [
                'en' => 'Analyzing the situation…',
                'no' => 'Analyserer situasjonen…',
                'pl' => 'Analiza sytuacji…',
                'uk' => 'Аналізую ситуацію…',
            ],
            'fetching_law' => [
                'en' => 'Fetching relevant legal sources…',
                'no' => 'Henter relevante lovkilder…',
                'pl' => 'Pobieranie odpowiednich źródeł prawnych…',
                'uk' => 'Завантажую відповідні юридичні джерела…',
            ],
            'drafting_no' => [
                'en' => 'Writing draft in Norwegian (bokmål)…',
                'no' => 'Skriver utkast på bokmål…',
                'pl' => 'Pisanie projektu po norwesku (bokmål)…',
                'uk' => 'Пишу чернетку норвезькою (bokmål)…',
            ],
            'quality_check' => [
                'en' => 'Quality-checking the draft…',
                'no' => 'Kvalitetskontroll av utkastet…',
                'pl' => 'Sprawdzanie jakości projektu…',
                'uk' => 'Перевірка якості чернетки…',
            ],
            'translating_to' => [
                'en' => 'Translating to {lang}…',
                'no' => 'Oversetter til {lang}…',
                'pl' => 'Tłumaczenie na {lang}…',
                'uk' => 'Переклад на {lang}…',
            ],
            'fetching_for_jur' => [
                'en' => 'Fetching authorities for {jur}…',
                'no' => 'Henter rettskilder for {jur}…',
                'pl' => 'Pobieranie autorytetów dla {jur}…',
                'uk' => 'Завантажую джерела для {jur}…',
            ],
            'rewriting_formal' => [
                'en' => 'Rewriting with formal citations…',
                'no' => 'Skriver om med formelle henvisninger…',
                'pl' => 'Przepisywanie z formalnymi cytatami…',
                'uk' => 'Переписую з формальними цитатами…',
            ],
            'check_and_authorities' => [
                'en' => 'Quality-check and Legal authorities block…',
                'no' => 'Kvalitetskontroll og Rettskilder…',
                'pl' => 'Kontrola jakości i blok źródeł prawnych…',
                'uk' => 'Перевірка якості і блок джерел права…',
            ],
            'file_read' => [
                'en' => 'Read {name} ({chars} chars)',
                'no' => 'Lest {name} ({chars} tegn)',
                'pl' => 'Przeczytano {name} ({chars} znaków)',
                'uk' => 'Прочитано {name} ({chars} символів)',
            ],
        ];
        $lang = in_array($lang, ['en', 'no', 'pl', 'uk'], true) ? $lang : 'en';
        $tmpl = $strings[$key][$lang] ?? $strings[$key]['en'] ?? $key;
        foreach ($vars as $k => $v) {
            $tmpl = str_replace('{' . $k . '}', (string)$v, $tmpl);
        }
        return $tmpl;
    }

    /** Localized jurisdiction labels for chrome (status messages). */
    public static function jurisdictionChromeLabel(string $jurisdiction, string $lang): string
    {
        $map = [
            'norwegian' => ['en' => 'Norwegian law', 'no' => 'norsk rett',           'pl' => 'prawo norweskie',     'uk' => 'норвезьке право'],
            'echr'      => ['en' => 'ECHR + HUDOC',  'no' => 'EMK og EMD-praksis',   'pl' => 'EKPC + HUDOC',         'uk' => 'ЄКПЛ + HUDOC'],
            'both'      => ['en' => 'NO + ECHR',     'no' => 'norsk rett + EMK/EMD', 'pl' => 'NO + EKPC',            'uk' => 'NO + ЄКПЛ'],
        ];
        $lang = in_array($lang, ['en', 'no', 'pl', 'uk'], true) ? $lang : 'en';
        return $map[$jurisdiction][$lang] ?? $map['norwegian']['en'];
    }

    /**
     * Pass 1 — extract structured facts and identify missing info.
     *
     * @return array{
     *   summary:string,
     *   parties:string[],
     *   decision_or_action:string,
     *   deadlines:string[],
     *   applicable_acts:string[],
     *   jurisdiction:?string,
     *   missing_facts:array<array{key:string,question:string}>,
     *   suggested_goal:?string
     * }
     */
    public function classify(array $intake): array
    {
        $body  = $intake['recipient_body'] ?? 'other';
        $mode  = $intake['mode'] ?? 'initiate';
        $bodyLabel = self::BODY_LABELS[$body] ?? 'mottaker';
        $userLang  = dbnToolsNormalizeUiLanguage($intake['language'] ?? 'en');
        $userLangName = dbnToolsLanguageName($userLang); // e.g. "English", "Norwegian", "Polish", "Ukrainian"

        $context = $this->buildContextBlob($intake);
        $modeLabel = $mode === 'reply' ? 'svar på et brev/vedtak' : 'innledning av en sak';

        $prompt = <<<PROMPT
You analyse a Norwegian correspondence drafting request. The user wants to write a {$modeLabel}
to {$bodyLabel}. Read the situation carefully and extract structured facts.

Return JSON only:
{
  "summary": "One-paragraph factual summary in Norwegian bokmål, neutral tone.",
  "parties": ["who the user is", "who the other party is", ...],
  "decision_or_action": "What the recipient did, or what the user wants the recipient to do",
  "deadlines": ["YYYY-MM-DD", "or relative deadline as plain text"],
  "applicable_acts": ["forvaltningsloven", "barnevernsloven", "NAV-loven", "opplæringslova", "barnehageloven", "EMK"],
  "jurisdiction": "kommune/fylke if known, else null",
  "missing_facts": [{"key":"deadline","question":"<question in {$userLangName}>"}],
  "suggested_goal": "One-line concrete goal for this letter, in Norwegian bokmål"
}

Rules:
- applicable_acts: pick ONLY from this controlled list, based on the recipient and situation:
  forvaltningsloven, barnevernsloven, NAV-loven, opplæringslova, barnehageloven, EMK, barneloven, sosialtjenesteloven
- missing_facts: include up to 4 items the drafter genuinely needs (date of decision, deadline, case
  number, specific decision being appealed, etc.). Leave EMPTY if intake is complete.
- For "reply" mode if no case reference is supplied, missing_facts SHOULD include one for it.
- IMPORTANT: write each missing-fact "question" field in **{$userLangName}**, short and clear.
  Do NOT write the question in Norwegian if the user's language is not Norwegian.

Intake:
{$context}
PROMPT;

        $default = [
            'summary'            => '',
            'parties'            => [],
            'decision_or_action' => '',
            'deadlines'          => [],
            'applicable_acts'    => ['forvaltningsloven'],
            'jurisdiction'       => null,
            'missing_facts'      => [],
            'suggested_goal'     => null,
        ];

        try {
            $raw = $this->azure->withDeployment(self::CLASSIFY_DEPLOYMENT)->chatText([
                ['role' => 'system', 'content' => 'You return valid JSON only. No markdown fences.'],
                ['role' => 'user',   'content' => $prompt],
            ], ['json' => true, 'temperature' => 0.1, 'max_tokens' => 800, 'timeout' => 30]);
            $json = $this->azure->decodeJsonObject($raw);
            if (!is_array($json)) {
                return $default;
            }
            return $this->normalizeClassify($json, $default);
        } catch (Throwable $e) {
            error_log('Korrespond classify failed: ' . $e->getMessage());
            return $default;
        }
    }

    /**
     * Pass 2 — retrieve law, draft, self-check, translate.
     *
     * @return array  Final result payload (matches NDJSON 'final' event shape).
     */
    public function generate(array $intake, array $classify, ?callable $emit = null): array
    {
        $body         = $intake['recipient_body'] ?? 'other';
        $outputType   = $intake['output_type'] ?? 'email';
        $tone         = $intake['tone'] ?? 'neutral';
        $userLang     = dbnToolsNormalizeUiLanguage($intake['language'] ?? 'en');
        $goal         = trim((string)($intake['goal'] ?? ($classify['suggested_goal'] ?? '')));
        $bodyLabel    = self::BODY_LABELS[$body] ?? 'mottaker';

        // ── Retrieve law ────────────────────────────────────────────────────────
        if ($emit) { $emit('progress', ['detail' => self::L('fetching_law', $userLang)]); }
        $retrieval = $this->retrieveLaw($body, $classify['applicable_acts'] ?? []);
        if ($emit) {
            $emit('retrieval', [
                'sources_count'   => count($retrieval['sources']),
                'applicable_acts' => $classify['applicable_acts'] ?? [],
            ]);
        }

        // ── Draft in Norwegian bokmål ───────────────────────────────────────────
        if ($emit) { $emit('progress', ['detail' => self::L('drafting_no', $userLang)]); }
        $draftNo = $this->draftNorwegian(
            $intake, $classify, $retrieval['sources'], $bodyLabel, $outputType, $tone, $goal
        );

        // ── Self-check: verify citations, deadline, goal, tone ──────────────────
        if ($emit) { $emit('progress', ['detail' => self::L('quality_check', $userLang)]); }
        $checked = $this->selfCheck($draftNo, $retrieval['sources'], $classify, $goal, $tone);

        // ── Legal check: dbn-legal-agent-v2 threshold verification ────────────────
        $legalCheck = [];
        try {
            $legalCheck = dbnToolsRunLegalCheck($checked['draft'], $body);
        } catch (Throwable $e) { /* silent — non-critical */ }

        // ── Translate to user language (if not Norwegian) ───────────────────────
        $draftUser = $checked['draft'];
        if ($userLang !== 'no') {
            if ($emit) { $emit('progress', ['detail' => self::L('translating_to', $userLang, ['lang' => dbnToolsLanguageName($userLang)])]); }
            $draftUser = $this->translate($checked['draft'], $userLang, $outputType);
        }

        return [
            'tool'          => 'korrespond',
            'language'      => $userLang,
            'mode'          => $intake['mode'] ?? 'initiate',
            'recipient_body'=> $body,
            'output_type'   => $outputType,
            'tone'          => $tone,
            'summary'       => $classify['summary'] ?? '',
            'goal'          => $goal,
            'draft_no'      => $checked['draft'],
            'draft_user'    => $draftUser,
            'draft_user_lang'=> $userLang,
            'cited_law'     => $checked['cited_sources'],
            'self_check'    => $checked['flags'],
            'legal_check'   => $legalCheck,
            'applicable_acts'=> $classify['applicable_acts'] ?? [],
            'deadlines'     => $classify['deadlines'] ?? [],
            'parties'       => $classify['parties'] ?? [],
            'disclaimer'    => dbnToolsDisclaimer($userLang),
        ];
    }

    // ── Helpers ─────────────────────────────────────────────────────────────────

    private function buildContextBlob(array $intake): string
    {
        $parts = [];
        $parts[] = 'Mottaker (recipient body): ' . (self::BODY_LABELS[$intake['recipient_body'] ?? 'other'] ?? 'mottaker');
        $parts[] = 'Modus: ' . (($intake['mode'] ?? 'initiate') === 'reply' ? 'svare på mottatt brev' : 'innlede sak');
        if (!empty($intake['case_ref'])) {
            $parts[] = 'Saksnummer: ' . $intake['case_ref'];
        }
        if (!empty($intake['where'])) {
            $parts[] = 'Sted (kommune/fylke): ' . $intake['where'];
        }
        if (!empty($intake['parties_text'])) {
            $parts[] = 'Parter:' . "\n" . $intake['parties_text'];
        }
        if (!empty($intake['deadlines']) && is_array($intake['deadlines'])) {
            $parts[] = 'Datoer/frister: ' . implode(', ', array_map('strval', $intake['deadlines']));
        }
        if (!empty($intake['goal'])) {
            $parts[] = 'Brukerens mål: ' . $intake['goal'];
        }
        if (!empty($intake['narrative'])) {
            $parts[] = 'Hva skjedde / kontekst:' . "\n" . $intake['narrative'];
        }
        if (!empty($intake['received_text'])) {
            $parts[] = 'Mottatt brev/vedtak:' . "\n" . mb_substr($intake['received_text'], 0, 8000, 'UTF-8');
        }
        if (!empty($intake['attachments_text'])) {
            $parts[] = 'Vedlegg (utdrag):' . "\n" . mb_substr($intake['attachments_text'], 0, 6000, 'UTF-8');
        }
        if (!empty($intake['clarifications']) && is_array($intake['clarifications'])) {
            $parts[] = 'Tilleggsopplysninger fra brukeren:';
            foreach ($intake['clarifications'] as $key => $value) {
                if (trim((string)$value) === '') continue;
                $parts[] = ' • ' . $key . ': ' . $value;
            }
        }

        // Inject Min Sak (private case corpus) hits if user opted in and is on a paid tier.
        if (!empty($intake['use_my_case'])) {
            $caseQuery = trim((string)($intake['goal'] ?? '') . ' ' . (string)($intake['narrative'] ?? ''));
            if ($caseQuery !== '') {
                $caseBlock = dbnToolsCaseContext(true, $caseQuery, 5);
                if ($caseBlock !== '') {
                    $parts[] = $caseBlock;
                }
            }
        }

        return mb_substr(implode("\n\n", $parts), 0, self::MAX_CONTEXT_CHARS, 'UTF-8');
    }

    private function normalizeClassify(array $json, array $default): array
    {
        $out = $default;
        if (is_string($json['summary'] ?? null))           $out['summary'] = trim($json['summary']);
        if (is_string($json['decision_or_action'] ?? null)) $out['decision_or_action'] = trim($json['decision_or_action']);
        if (is_string($json['jurisdiction'] ?? null) && trim($json['jurisdiction']) !== '') {
            $out['jurisdiction'] = trim($json['jurisdiction']);
        }
        if (is_string($json['suggested_goal'] ?? null) && trim($json['suggested_goal']) !== '') {
            $out['suggested_goal'] = trim($json['suggested_goal']);
        }
        if (is_array($json['parties'] ?? null)) {
            $out['parties'] = array_values(array_filter(array_map(
                fn($p) => trim((string)$p),
                $json['parties']
            )));
        }
        if (is_array($json['deadlines'] ?? null)) {
            $out['deadlines'] = array_values(array_filter(array_map(
                fn($d) => trim((string)$d),
                $json['deadlines']
            )));
        }
        if (is_array($json['applicable_acts'] ?? null)) {
            $allowed = ['forvaltningsloven','barnevernsloven','NAV-loven','opplæringslova',
                'barnehageloven','EMK','barneloven','sosialtjenesteloven'];
            $out['applicable_acts'] = array_values(array_intersect($allowed,
                array_map(fn($a) => trim((string)$a), $json['applicable_acts'])));
            if (empty($out['applicable_acts'])) {
                $out['applicable_acts'] = ['forvaltningsloven'];
            }
        }
        if (is_array($json['missing_facts'] ?? null)) {
            $mf = [];
            foreach ($json['missing_facts'] as $item) {
                if (!is_array($item)) continue;
                $k = trim((string)($item['key'] ?? ''));
                $q = trim((string)($item['question'] ?? ''));
                if ($k === '' || $q === '') continue;
                $mf[] = ['key' => $k, 'question' => $q];
                if (count($mf) >= 4) break;
            }
            $out['missing_facts'] = $mf;
        }
        return $out;
    }

    /**
     * Hard-RAG retrieval: pull law passages via ClientRagPipeline, filtered
     * to slice presets for the recipient body. Numbered sources for citation tokens.
     *
     * @return array{sources:array, applied_slices:string[]}
     */
    private function retrieveLaw(string $body, array $applicableActs): array
    {
        $client  = dbnToolsRequireClient();
        $package = dbnToolsFetchPackage(dbnToolsRequiredPackageSlug());
        if (!$package) {
            return ['sources' => [], 'applied_slices' => []];
        }

        dbnToolsBootCaveau();
        $aiPortalRoot = dbnToolsAiPortalRoot();
        $v6 = $aiPortalRoot . '/platform/includes/dbn_v6.php';
        if (is_file($v6)) {
            require_once $v6;
        }

        // Slice preset for this body type
        $sliceIds = self::BODY_PRESETS[$body] ?? self::BODY_PRESETS['other'];
        $sliceSel = [];
        foreach ($sliceIds as $sid) { $sliceSel[$sid] = true; }
        $sliceSel = function_exists('dbnV6NormalizeSliceSelection')
            ? dbnV6NormalizeSliceSelection($sliceSel)
            : $sliceSel;

        $ragDb        = dbnToolsRagDb();
        $sharedDocIds = [];
        if (function_exists('dbnV6ResolveSelectedDocIds')) {
            try {
                $sharedDocIds = dbnV6ResolveSelectedDocIds($ragDb, $sliceSel);
            } catch (Throwable $e) {
                error_log('Korrespond slice resolve failed: ' . $e->getMessage());
                $sharedDocIds = [];
            }
        }

        // Build 2-3 retrieval queries: one per applicable act
        $queries = [];
        foreach ($applicableActs as $act) {
            $queries[] = $this->queryForAct($act);
        }
        if (empty($queries)) {
            $queries = ['forvaltningsloven prosessuelle rettigheter saksbehandling'];
        }
        $queries = array_slice(array_unique($queries), 0, 3);

        // Run hybrid retrieval via ClientRagPipeline
        $pool = [];
        try {
            if (!class_exists('ClientRagPipeline')) {
                return ['sources' => [], 'applied_slices' => array_keys(array_filter($sliceSel))];
            }
            $rag = new ClientRagPipeline((int)$client['id'], 'http://10.0.1.10:4000', 60);
            foreach ($queries as $q) {
                try {
                    $chunks = $rag->searchAll($q, 5, null, [
                        'search_private'          => false,
                        'search_shared'           => true,
                        'package_ids'             => [(int)$package['id']],
                        'shared_doc_ids'          => $sharedDocIds,
                        'chunk_limit'             => 5,
                        'search_method'           => 'hybrid',
                        'reranker_enabled'        => true,
                        'include_beta_website'    => false,
                        'include_primary_website' => false,
                    ]);
                } catch (Throwable $e) {
                    error_log('Korrespond rag search failed: ' . $e->getMessage());
                    $chunks = [];
                }
                foreach ($chunks as $c) {
                    $pool[] = $c;
                }
            }
        } catch (Throwable $e) {
            error_log('Korrespond rag init failed: ' . $e->getMessage());
        }

        // Dedupe by chunk_id, number sequentially
        $seen = [];
        $sources = [];
        $n = 1;
        foreach ($pool as $c) {
            $cid = (string)($c['chunk_id'] ?? $c['id'] ?? '');
            if ($cid === '' || isset($seen[$cid])) continue;
            $seen[$cid] = true;
            $text = (string)($c['chunk_text'] ?? $c['content'] ?? $c['text'] ?? '');
            $title = (string)($c['document_title'] ?? $c['title'] ?? 'Lovkilde');
            $section = (string)($c['section_title'] ?? $c['section'] ?? '');
            $sources[] = [
                'n'        => $n++,
                'chunk_id' => $cid,
                'title'    => $title,
                'section'  => $section,
                'excerpt'  => dbnToolsExcerpt($text, 500),
                'full_text'=> mb_substr($text, 0, 1800, 'UTF-8'),
                'source_url' => (string)($c['source_url'] ?? $c['url'] ?? ''),
            ];
            if (count($sources) >= 8) break;
        }

        return [
            'sources'        => $sources,
            'applied_slices' => array_keys(array_filter($sliceSel)),
        ];
    }

    private function queryForAct(string $act): string
    {
        return match (strtolower($act)) {
            'forvaltningsloven'  => 'forvaltningsloven §17 §18 §24 §25 §28 §32 saksbehandling kontradiksjon innsyn klage',
            'barnevernsloven'    => 'barnevernsloven omsorgsovertakelse akuttvedtak undersøkelse saksbehandling',
            'nav-loven'          => 'NAV-loven folketrygdloven klage anke vedtak saksbehandling',
            'opplæringslova'     => 'opplæringslova spesialundervisning enkeltvedtak klage skole',
            'barnehageloven'     => 'barnehageloven enkeltvedtak klage tilbud spesialpedagogisk',
            'emk'                => 'EMK artikkel 6 artikkel 8 familieliv rettferdig rettergang',
            'barneloven'         => 'barneloven samvær foreldreansvar fast bosted',
            'sosialtjenesteloven'=> 'sosialtjenesteloven økonomisk stønad kvalifiseringsprogram klage',
            default              => 'forvaltningsloven saksbehandling klage',
        };
    }

    private function draftNorwegian(
        array $intake, array $classify, array $sources, string $bodyLabel,
        string $outputType, string $tone, string $goal
    ): string {
        $context     = $this->buildContextBlob($intake);
        $toneLabel   = $this->toneLabelNorsk($tone);
        $outputBlock = $this->outputInstructionsNorsk($outputType, $bodyLabel);

        $sourcesBlock = '';
        if (!empty($sources)) {
            $rows = [];
            foreach ($sources as $s) {
                $rows[] = sprintf("[id=%d] %s%s\n%s",
                    (int)$s['n'],
                    $s['title'],
                    $s['section'] !== '' ? ' — ' . $s['section'] : '',
                    $s['excerpt']
                );
            }
            $sourcesBlock = "RETRIEVED LAW PASSAGES (you may ONLY cite these):\n" . implode("\n\n", $rows);
        } else {
            $sourcesBlock = 'RETRIEVED LAW PASSAGES: (none — write a plain-language draft without § references)';
        }

        $goalLine = $goal !== '' ? ('Brukerens mål: ' . $goal) : 'Brukerens mål: ikke spesifisert — utled fra konteksten.';

        $prompt = <<<PROMPT
Du skriver utkast til korrespondanse til {$bodyLabel}. Skriv på norsk bokmål.
Tone: {$toneLabel}.

{$goalLine}

{$outputBlock}

REGLER FOR LOVHENVISNINGER (kritisk):
- Du har KUN lov til å sitere §-er eller artikler som finnes i de hentede lovpassasjene under.
- Hver gang du siterer en §, MÅ du legge ved en token på formen [CITE:N] der N er id-nummeret
  fra passasjelisten. Eksempel: "etter forvaltningsloven § 17 [CITE:2]".
- Hvis du ikke finner dekning i passasjene, skriv UTEN §-henvisning.
- IKKE finn på §-nummer eller artikkelnumre.

{$sourcesBlock}

KONTEKST (intake fra brukeren):
{$context}

Skriv kun utkastet. Ingen forklaring eller preamble. Bruk linjeskift som passer formatet.
PROMPT;

        try {
            return $this->azure->withDeployment(self::DRAFT_DEPLOYMENT)->chatText([
                ['role' => 'system', 'content' => 'Du er en erfaren norsk juridisk forfatter som skriver presist og faktabasert.'],
                ['role' => 'user',   'content' => $prompt],
            ], [
                'temperature' => 0.25,
                'max_tokens'  => self::MAX_DRAFT_TOKENS,
                'timeout'     => 120,
            ]);
        } catch (Throwable $e) {
            error_log('Korrespond draft failed: ' . $e->getMessage());
            return '[Utkast kunne ikke genereres. Prøv igjen.]';
        }
    }

    private function selfCheck(string $draft, array $sources, array $classify, string $goal, string $tone): array
    {
        // Strip any [CITE:N] tokens that don't map to a real source
        $validIds = array_map(fn($s) => (int)$s['n'], $sources);
        $cited = [];
        $cleanedDraft = preg_replace_callback('/\[CITE:(\d+)\]/', function ($m) use ($validIds, &$cited) {
            $id = (int)$m[1];
            if (in_array($id, $validIds, true)) {
                $cited[$id] = true;
                return '[' . $id . ']';
            }
            return ''; // strip unverified
        }, $draft) ?? $draft;

        // Strip orphan § references whose nearest CITE was removed: lightweight heuristic —
        // leave §-text intact but flag if there are § references with NO surviving [N] near them.
        $hasParagraph = (bool)preg_match('/§\s*\d|artikkel\s*\d/iu', $cleanedDraft);
        $hasCitation  = !empty($cited);

        // Deadline check: if classify found deadlines, expect at least one mention in draft
        $deadlineMentioned = true;
        if (!empty($classify['deadlines'])) {
            $deadlineMentioned = false;
            foreach ($classify['deadlines'] as $d) {
                if ($d !== '' && mb_stripos($cleanedDraft, $d) !== false) {
                    $deadlineMentioned = true;
                    break;
                }
            }
            if (!$deadlineMentioned && preg_match('/frist|innen|dato|deadline/iu', $cleanedDraft)) {
                $deadlineMentioned = true;
            }
        }

        // Goal mention check — relax: just check the draft is non-trivial
        $goalAddressed = mb_strlen(trim($cleanedDraft), 'UTF-8') > 150;

        $flags = [
            'citations_verified' => $hasParagraph ? ($hasCitation ? 'ok' : 'warn') : 'ok',
            'deadline_mentioned' => $deadlineMentioned ? 'ok' : 'warn',
            'goal_addressed'     => $goalAddressed ? 'ok' : 'warn',
            'tone'               => 'ok', // tone is set by prompt; trust it
        ];

        // Map cited ids → source records
        $citedSources = [];
        foreach ($sources as $s) {
            if (isset($cited[(int)$s['n']])) {
                $citedSources[] = $s;
            }
        }

        return [
            'draft'          => trim($cleanedDraft),
            'cited_sources'  => $citedSources,
            'flags'          => $flags,
        ];
    }

    private function translate(string $norwegianDraft, string $userLang, string $outputType): string
    {
        $target = dbnToolsLanguageName($userLang);
        $format = $this->outputFormatHintEnglish($outputType);
        $prompt = <<<PROMPT
Translate the Norwegian (bokmål) {$format} below into {$target}.
- Preserve all numeric bracket citations like [3] exactly where they appear.
- Preserve § references and any case/reference numbers verbatim.
- Keep paragraph structure and tone.
- Do not add commentary.

Norwegian source:
{$norwegianDraft}
PROMPT;

        try {
            return $this->azure->withDeployment(self::SELFCHECK_DEPLOYMENT)->chatText([
                ['role' => 'system', 'content' => 'You are a precise legal translator.'],
                ['role' => 'user',   'content' => $prompt],
            ], [
                'temperature' => 0.1,
                'max_tokens'  => self::MAX_DRAFT_TOKENS,
                'timeout'     => 90,
            ]);
        } catch (Throwable $e) {
            error_log('Korrespond translate failed: ' . $e->getMessage());
            return $norwegianDraft; // fallback: return NO unchanged
        }
    }

    private function toneLabelNorsk(string $tone): string
    {
        return match ($tone) {
            'cooperative'  => 'samarbeidsorientert, høflig, men presist',
            'firm'         => 'fast og bestemt, men korrekt og uten anklagende språk',
            'adversarial'  => 'tydelig konfronterende, varsler videre rettslige skritt',
            'warm'         => 'forsonende og varm, anerkjenner motpartens situasjon',
            default        => 'nøytral og profesjonell',
        };
    }

    private function outputInstructionsNorsk(string $outputType, string $bodyLabel): string
    {
        return match ($outputType) {
            'email' => <<<EOT
FORMAT: E-post.
- Begynn med "Til: {$bodyLabel}" og "Emne: …".
- Skriv en kort innledning, faktiske forhold, juridisk grunnlag (med [CITE:N]-tokens), konkret anmodning og frist for tilbakemelding.
- Avslutt med "Med vennlig hilsen, [Navn]".
EOT,
            'formal' => <<<EOT
FORMAT: Formelt brev (utskriftsklart).
- Topplinje: avsender (plassholder), mottaker ({$bodyLabel}), sted og dato.
- Emnelinje med saksnummer hvis kjent.
- Punkter: 1. Saksforhold, 2. Rettslig grunnlag (med [CITE:N]), 3. Anmodning/krav, 4. Frist, 5. Underskrift.
EOT,
            'filing' => <<<EOT
FORMAT: Prosesskriv/klage til {$bodyLabel}.
- Topplinje: Til {$bodyLabel}, saksnummer, dato.
- Påstand (hva som kreves), saksforhold (faktiske forhold), anførsler (rettslig grunnlag med [CITE:N]), bevis, sluttpåstand.
- Bruk juridisk struktur, men hold språket klart.
EOT,
            'call_prep' => <<<EOT
FORMAT: Forberedelse til telefonsamtale (interne notater til brukeren, IKKE et brev).
Bruk overskrifter:
- "Åpningslinje:" (én setning brukeren kan si først)
- "Nøkkelfakta å nevne:" (3–5 punkter)
- "Lover å vise til hvis presset:" (med [CITE:N])
- "Spørsmål å stille:" (3–5 punkter)
- "Hvis de nekter — neste skritt:" (eskaleringsvei: Statsforvalter / klage / advokat)
EOT,
            default => 'FORMAT: E-post (standard).',
        };
    }

    private function outputFormatHintEnglish(string $outputType): string
    {
        return match ($outputType) {
            'email'     => 'email',
            'formal'    => 'formal letter',
            'filing'    => 'court/tribunal filing',
            'call_prep' => 'phone-call preparation notes',
            default     => 'letter',
        };
    }

    // ── Pass 3: refine with jurisdiction-scoped formal citations ────────────────

    /**
     * Refine an existing draft by retrieving law scoped to the chosen jurisdiction,
     * rewriting inline citations into a formal style, and appending a Rettskilder
     * (legal authorities) block.
     *
     * @param array  $intake         Original intake (recipient_body, output_type, tone, language, …)
     * @param array  $classify       Pass 1 classify result (summary, applicable_acts, deadlines, …)
     * @param string $originalDraftNo  The Norwegian draft from Pass 2
     * @param string $jurisdiction   'norwegian' | 'echr' | 'both'
     * @param callable|null $emit    Stream emitter
     *
     * @return array  Final refined result payload.
     */
    public function refine(
        array $intake,
        array $classify,
        string $originalDraftNo,
        string $jurisdiction,
        ?callable $emit = null
    ): array {
        $body         = $intake['recipient_body'] ?? 'other';
        $outputType   = $intake['output_type'] ?? 'email';
        $tone         = $intake['tone'] ?? 'neutral';
        $userLang     = dbnToolsNormalizeUiLanguage($intake['language'] ?? 'en');
        $goal         = trim((string)($intake['goal'] ?? ($classify['suggested_goal'] ?? '')));
        $bodyLabel    = self::BODY_LABELS[$body] ?? 'mottaker';
        $jurisdiction = in_array($jurisdiction, ['norwegian', 'echr', 'both'], true) ? $jurisdiction : 'norwegian';

        if ($emit) { $emit('progress', ['detail' => self::L('fetching_for_jur', $userLang, ['jur' => self::jurisdictionChromeLabel($jurisdiction, $userLang)])]); }
        $retrieval = $this->retrieveLawForJurisdiction($jurisdiction, $body, $classify);
        if ($emit) {
            $emit('retrieval', [
                'sources_count'   => count($retrieval['sources']),
                'jurisdiction'    => $jurisdiction,
                'applied_slices'  => $retrieval['applied_slices'],
            ]);
        }

        if ($emit) { $emit('progress', ['detail' => self::L('rewriting_formal', $userLang)]); }
        $refinedNo = $this->rewriteWithFormalCites(
            $originalDraftNo, $retrieval['sources'], $bodyLabel, $outputType, $tone, $goal, $jurisdiction
        );

        if ($emit) { $emit('progress', ['detail' => self::L('check_and_authorities', $userLang)]); }
        $checked = $this->selfCheck($refinedNo, $retrieval['sources'], $classify, $goal, $tone);

        $legalCheckRefine = [];
        try {
            $legalCheckRefine = dbnToolsRunLegalCheck($checked['draft'], $body);
        } catch (Throwable $e) { /* silent — non-critical */ }

        $draftUser = $checked['draft'];
        if ($userLang !== 'no') {
            if ($emit) { $emit('progress', ['detail' => self::L('translating_to', $userLang, ['lang' => dbnToolsLanguageName($userLang)])]); }
            $draftUser = $this->translate($checked['draft'], $userLang, $outputType);
        }

        return [
            'tool'           => 'korrespond_refine',
            'language'       => $userLang,
            'jurisdiction'   => $jurisdiction,
            'recipient_body' => $body,
            'output_type'    => $outputType,
            'tone'           => $tone,
            'draft_no'       => $checked['draft'],
            'draft_user'     => $draftUser,
            'draft_user_lang'=> $userLang,
            'cited_law'      => $checked['cited_sources'],
            'self_check'     => $checked['flags'],
            'legal_check'    => $legalCheckRefine,
            'applied_slices' => $retrieval['applied_slices'],
            'disclaimer'     => dbnToolsDisclaimer($userLang),
        ];
    }

    /**
     * Retrieve law scoped to user's jurisdiction choice.
     * - norwegian: body preset slices MINUS echr
     * - echr:      only echr slice; queries target EMK articles + HUDOC case law
     * - both:      union (full body preset + extra ECHR queries)
     *
     * @return array{sources:array, applied_slices:string[]}
     */
    private function retrieveLawForJurisdiction(string $jurisdiction, string $body, array $classify): array
    {
        $client  = dbnToolsRequireClient();
        $package = dbnToolsFetchPackage(dbnToolsRequiredPackageSlug());
        if (!$package) {
            return ['sources' => [], 'applied_slices' => []];
        }

        dbnToolsBootCaveau();
        $aiPortalRoot = dbnToolsAiPortalRoot();
        $v6 = $aiPortalRoot . '/platform/includes/dbn_v6.php';
        if (is_file($v6)) {
            require_once $v6;
        }

        $bodyPreset = self::BODY_PRESETS[$body] ?? self::BODY_PRESETS['other'];

        // Build slice selection per jurisdiction
        $sliceSel = [];
        if ($jurisdiction === 'norwegian') {
            foreach ($bodyPreset as $s) { if ($s !== 'echr') { $sliceSel[$s] = true; } }
            if (empty($sliceSel)) { $sliceSel['family_core'] = true; }
        } elseif ($jurisdiction === 'echr') {
            $sliceSel['echr'] = true;
        } else { // both
            foreach ($bodyPreset as $s) { $sliceSel[$s] = true; }
            $sliceSel['echr'] = true;
        }

        $sliceSel = function_exists('dbnV6NormalizeSliceSelection')
            ? dbnV6NormalizeSliceSelection($sliceSel)
            : $sliceSel;

        $ragDb        = dbnToolsRagDb();
        $sharedDocIds = [];
        if (function_exists('dbnV6ResolveSelectedDocIds')) {
            try {
                $sharedDocIds = dbnV6ResolveSelectedDocIds($ragDb, $sliceSel);
            } catch (Throwable $e) {
                error_log('Korrespond refine slice resolve failed: ' . $e->getMessage());
                $sharedDocIds = [];
            }
        }

        // Build retrieval queries
        $queries = $this->refineQueries($jurisdiction, $body, $classify);

        $pool = [];
        try {
            if (!class_exists('ClientRagPipeline')) {
                return ['sources' => [], 'applied_slices' => array_keys(array_filter($sliceSel))];
            }
            $rag = new ClientRagPipeline((int)$client['id'], 'http://10.0.1.10:4000', 60);
            foreach ($queries as $q) {
                try {
                    $chunks = $rag->searchAll($q, 5, null, [
                        'search_private'          => false,
                        'search_shared'           => true,
                        'package_ids'             => [(int)$package['id']],
                        'shared_doc_ids'          => $sharedDocIds,
                        'chunk_limit'             => 5,
                        'search_method'           => 'hybrid',
                        'reranker_enabled'        => true,
                        'include_beta_website'    => false,
                        'include_primary_website' => false,
                    ]);
                } catch (Throwable $e) {
                    error_log('Korrespond refine rag search failed: ' . $e->getMessage());
                    $chunks = [];
                }
                foreach ($chunks as $c) {
                    $pool[] = $c;
                }
            }
        } catch (Throwable $e) {
            error_log('Korrespond refine rag init failed: ' . $e->getMessage());
        }

        // Dedupe + number — bump source cap to 12 since refine cites more authorities
        $seen = [];
        $sources = [];
        $n = 1;
        foreach ($pool as $c) {
            $cid = (string)($c['chunk_id'] ?? $c['id'] ?? '');
            if ($cid === '' || isset($seen[$cid])) continue;
            $seen[$cid] = true;
            $text  = (string)($c['chunk_text'] ?? $c['content'] ?? $c['text'] ?? '');
            $title = (string)($c['document_title'] ?? $c['title'] ?? 'Lovkilde');
            $section = (string)($c['section_title'] ?? $c['section'] ?? '');
            $sources[] = [
                'n'         => $n++,
                'chunk_id'  => $cid,
                'title'     => $title,
                'section'   => $section,
                'excerpt'   => dbnToolsExcerpt($text, 500),
                'full_text' => mb_substr($text, 0, 1800, 'UTF-8'),
                'source_url'=> (string)($c['source_url'] ?? $c['url'] ?? ''),
                'authority_type' => (string)($c['authority_type'] ?? ''),
            ];
            if (count($sources) >= 12) break;
        }

        return [
            'sources'        => $sources,
            'applied_slices' => array_keys(array_filter($sliceSel)),
        ];
    }

    /** Build 2–4 retrieval queries appropriate for the jurisdiction. */
    private function refineQueries(string $jurisdiction, string $body, array $classify): array
    {
        $queries = [];
        $summaryHint = mb_substr((string)($classify['summary'] ?? ''), 0, 220, 'UTF-8');

        if ($jurisdiction === 'norwegian' || $jurisdiction === 'both') {
            foreach (($classify['applicable_acts'] ?? []) as $act) {
                if (strtolower($act) === 'emk') continue; // handled in ECHR branch
                $queries[] = $this->queryForAct($act);
            }
            if (empty(array_filter($queries))) {
                $queries[] = 'forvaltningsloven saksbehandling klage';
            }
        }

        if ($jurisdiction === 'echr' || $jurisdiction === 'both') {
            // Always pull EMK articles + general HUDOC vs Norway case law
            $queries[] = 'EMK artikkel 8 familieliv rettferdig rettergang';
            $queries[] = 'EMD HUDOC Norway family life article 8 case law violation';
            // Body-specific high-value case anchors
            if (in_array($body, ['barnevernet', 'bufdir', 'statsforvalter', 'tingrett'], true)) {
                $queries[] = 'Strand Lobben Norway adoption family ties biological';
            }
            if ($summaryHint !== '') {
                $queries[] = $summaryHint . ' EMK EMD case law';
            }
        }

        $queries = array_values(array_unique(array_filter(array_map('trim', $queries))));
        return array_slice($queries, 0, 5);
    }

    private function rewriteWithFormalCites(
        string $originalDraft,
        array  $sources,
        string $bodyLabel,
        string $outputType,
        string $tone,
        string $goal,
        string $jurisdiction
    ): string {
        $toneLabel   = $this->toneLabelNorsk($tone);
        $jurLabel    = $this->jurisdictionLabelNorsk($jurisdiction);
        $outputBlock = $this->outputInstructionsNorsk($outputType, $bodyLabel);

        $sourcesBlock = '';
        if (!empty($sources)) {
            $rows = [];
            foreach ($sources as $s) {
                $authority = $s['authority_type'] !== '' ? ' (' . $s['authority_type'] . ')' : '';
                $rows[] = sprintf("[id=%d] %s%s%s\n%s",
                    (int)$s['n'],
                    $s['title'],
                    $s['section'] !== '' ? ' — ' . $s['section'] : '',
                    $authority,
                    $s['excerpt']
                );
            }
            $sourcesBlock = "RETRIEVED LEGAL AUTHORITIES (you may ONLY cite these):\n" . implode("\n\n", $rows);
        } else {
            $sourcesBlock = 'RETRIEVED LEGAL AUTHORITIES: (none — keep the existing inline references without expansion)';
        }

        $goalLine = $goal !== '' ? ('Brukerens mål: ' . $goal) : 'Brukerens mål: utled fra utkastet.';

        $prompt = <<<PROMPT
Du skal SKRIVE OM utkastet under slik at lovhenvisninger blir formelle og presise.
Skriv på norsk bokmål. Tone: {$toneLabel}. Rettsområde: {$jurLabel}.

{$goalLine}

{$outputBlock}

REGLER FOR FORMELLE HENVISNINGER (kritisk):
- Bevar utkastets struktur, fakta og overordnede budskap. Endre kun stilen på henvisningene.
- For norske lover: bruk formelle henvisninger som "jf. forvaltningsloven § 17", "i medhold av barnevernsloven § 6-3", "etter opplæringslova § 9 A-4".
- For EMK-artikler: "jf. EMK artikkel 8", "etter EMK art. 6 nr. 1".
- For EMD-dommer: bruk fullt navn + saksnummer + dato + paragraf, f.eks.
    "jf. Strand Lobben m.fl. mot Norge, EMD-37283/13, dom 10. september 2019, §§ 207–214"
- Hver gang du siterer en autoritet, MÅ du legge ved [CITE:N] der N er id-nummeret fra
  listen under. Bare disse autoritetene er tillatt. IKKE finn på saksnummer, datoer eller §-numre.
- Hvis det opprinnelige utkastet siterte noe som ikke finnes i autoritetslisten, omformuler
  uten den henvisningen.

PÅ SLUTTEN av utkastet — etter signaturen / sluttpåstanden — legg til en blokk:

Rettskilder
-----------
[1] Full formell referanse for autoritet 1
[2] Full formell referanse for autoritet 2
...

Listen skal kun inneholde autoriteter som faktisk er sitert i utkastet over.

{$sourcesBlock}

OPPRINNELIG UTKAST (skriv om dette):
{$originalDraft}

Returner kun det omskrevne utkastet med Rettskilder-blokken. Ingen forklaring eller preamble.
PROMPT;

        try {
            return $this->azure->withDeployment(self::DRAFT_DEPLOYMENT)->chatText([
                ['role' => 'system', 'content' => 'Du er en erfaren norsk juridisk skribent som skriver presise prosesskriv med formelle rettshenvisninger.'],
                ['role' => 'user',   'content' => $prompt],
            ], [
                'temperature' => 0.2,
                'max_tokens'  => self::MAX_DRAFT_TOKENS,
                'timeout'     => 120,
            ]);
        } catch (Throwable $e) {
            error_log('Korrespond refine rewrite failed: ' . $e->getMessage());
            return $originalDraft;
        }
    }

    private function jurisdictionLabelNorsk(string $jurisdiction): string
    {
        return match ($jurisdiction) {
            'norwegian' => 'norsk rett',
            'echr'      => 'EMK og EMD-praksis',
            'both'      => 'norsk rett + EMK/EMD',
            default     => 'norsk rett',
        };
    }
}
