<?php

namespace App\Components\Client\Concerns;

use App\Models\Client\Client;
use App\Models\Witness\WitnessStatement;
use App\Models\Witness\WitnessStatementAnswer;
use App\Services\Witness\WitnessStatementService;
use App\Support\Witness\WitnessQuestionnaire;
use Illuminate\Support\Str;

/**
 * Shared Livewire behaviour for every Witness Statement concept. All four UI
 * concepts `use` this trait, so they share one authoritative set of state,
 * actions and view data — the concepts differ only in layout/interaction, not
 * in behaviour. Business logic lives in WitnessStatementService; this trait is
 * the thin Livewire glue.
 *
 * The host component must be Livewire and use AuthorizesRequests.
 */
trait InteractsWithWitnessStatement
{
    public Client $client;

    public int $statementId;

    /** @var array<string, array<string, mixed>> buffer keyed [section][question] */
    public array $answers = [];

    /** @var array<string, bool> selected generation options */
    public array $options = [];

    public ?string $activeSectionKey = null;

    /** Force a visual state for the design exercise: empty|loading|completed|validation|missing */
    public ?string $demoState = null;

    /** Whether this render is part of the design exercise (shows the concept chrome/banner). */
    public bool $designMode = true;

    /** For inline editing of a suggestion before accepting. */
    public ?int $editingAnswerId = null;

    public string $editingValue = '';

    /** Validation stays neutral until requested (continue / generate) or a field is touched. */
    public bool $validationRequested = false;

    /** @var array<int, string> "section.question" keys the user has interacted with */
    public array $touched = [];

    /** Transient autosave indicator: ''|saving|saved|failed (saving is shown via wire:loading). */
    public string $saveStatus = '';

    /** Explanatory suggestion guidance can be dismissed once understood. */
    public bool $guidanceDismissed = false;

    /** Evidence rail: show only the active section's evidence, or everything. */
    public bool $showAllEvidence = false;

    public function bootWitnessWorkspace(Client $client): void
    {
        $this->client = $client;

        if (! $client->claim) {
            // No claim → nothing to attach a statement to. Concepts render an empty state.
            $this->statementId = 0;

            return;
        }

        $ws = $this->service()->resolve($client->claim);
        $this->authorize('view', $ws);

        $this->statementId = $ws->id;
        $this->loadBuffers($ws);
        $this->activeSectionKey ??= $this->questionnaire()->sections()->first()?->key;
    }

    // ── actions ──────────────────────────────────────────────────────────────

    public function saveAnswer(string $sectionKey, string $questionKey): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $this->markTouched($sectionKey, $questionKey);
        $value = $this->answers[$sectionKey][$questionKey] ?? null;

        try {
            $this->service()->saveAnswer($ws, $sectionKey, $questionKey, $value);
            $this->saveStatus = 'saved';
        } catch (\Throwable $e) {
            $this->saveStatus = 'failed';
            report($e);
        }
    }

    /**
     * Save a boolean answer directly (keeps a real bool in storage, which the
     * eligibility-conflict checks rely on).
     */
    public function saveBoolean(string $sectionKey, string $questionKey, bool $value): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $this->markTouched($sectionKey, $questionKey);
        $this->answers[$sectionKey][$questionKey] = $value;

        try {
            $this->service()->saveAnswer($ws, $sectionKey, $questionKey, $value);
            $this->saveStatus = 'saved';
        } catch (\Throwable $e) {
            $this->saveStatus = 'failed';
            report($e);
        }
    }

    protected function markTouched(string $sectionKey, string $questionKey): void
    {
        $key = $sectionKey.'.'.$questionKey;
        if (! in_array($key, $this->touched, true)) {
            $this->touched[] = $key;
        }
    }

    public function addRepeaterRow(string $sectionKey, string $questionKey): void
    {
        $this->answers[$sectionKey][$questionKey][] = [];
    }

    public function removeRepeaterRow(string $sectionKey, string $questionKey, int $index): void
    {
        if (isset($this->answers[$sectionKey][$questionKey][$index])) {
            unset($this->answers[$sectionKey][$questionKey][$index]);
            $this->answers[$sectionKey][$questionKey] = array_values($this->answers[$sectionKey][$questionKey]);
            $this->saveAnswer($sectionKey, $questionKey);
        }
    }

    public function acceptSuggestion(int $answerId): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $answer = $this->answerFor($ws, $answerId);
        $this->service()->acceptSuggestion($ws, $answer);
        $this->loadBuffers($ws->fresh());

        $this->dispatch('toast', type: 'success', message: 'Suggestion accepted.');
    }

    public function rejectSuggestion(int $answerId): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $answer = $this->answerFor($ws, $answerId);
        $this->service()->rejectSuggestion($ws, $answer);
        $this->loadBuffers($ws->fresh());

        $this->dispatch('toast', type: 'info', message: 'Suggestion rejected.');
    }

    public function startEditingSuggestion(int $answerId): void
    {
        $answer = $this->answerFor($this->statement(), $answerId);
        $this->editingAnswerId = $answerId;
        $this->editingValue = is_array($answer->value) ? '' : (string) $answer->value;
    }

    public function saveEditedSuggestion(): void
    {
        if (! $this->editingAnswerId) {
            return;
        }

        $ws = $this->statement();
        $this->authorize('update', $ws);

        $answer = $this->answerFor($ws, $this->editingAnswerId);
        $this->service()->replaceSuggestion($ws, $answer, $this->editingValue);

        $this->editingAnswerId = null;
        $this->editingValue = '';
        $this->loadBuffers($ws->fresh());

        $this->dispatch('toast', type: 'success', message: 'Suggestion updated.');
    }

    public function cancelEditingSuggestion(): void
    {
        $this->editingAnswerId = null;
        $this->editingValue = '';
    }

    public function addExhibit(int $evidenceItemId): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $item = $this->service()->confirmedEvidence($ws)->firstWhere('id', $evidenceItemId);
        if (! $item) {
            $this->dispatch('toast', type: 'error', message: 'Evidence item not found.');

            return;
        }

        $this->service()->addExhibit($ws, [
            'dsar_evidence_item_id' => $item->id,
            'document_id' => $item->document_id,
            'page' => $item->page,
            'label' => Str::limit((string) $item->quote, 60),
        ]);

        $this->dispatch('toast', type: 'success', message: 'Exhibit added.');
    }

    public function removeExhibit(int $exhibitId): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $exhibit = $ws->exhibits()->findOrFail($exhibitId);
        $this->service()->removeExhibit($ws, $exhibit);

        $this->dispatch('toast', type: 'info', message: 'Exhibit removed.');
    }

    /**
     * Star toggle used by the Exhibits panel: include a confirmed finding as an
     * exhibit, or remove it if its document page is already exhibited.
     */
    public function toggleExhibit(int $evidenceItemId): void
    {
        $ws = $this->statement();
        $this->authorize('update', $ws);

        $item = $this->service()->confirmedEvidence($ws)->firstWhere('id', $evidenceItemId);
        if (! $item) {
            $this->dispatch('toast', type: 'error', message: 'Evidence item not found.');

            return;
        }

        $existing = $ws->exhibits()
            ->where('dsar_evidence_item_id', $item->id)
            ->first();

        if ($existing) {
            $this->service()->removeExhibit($ws, $existing);
            $this->dispatch('toast', type: 'info', message: 'Exhibit removed.');

            return;
        }

        $this->service()->addExhibit($ws, [
            'dsar_evidence_item_id' => $item->id,
            'document_id' => $item->document_id,
            'page' => $item->page,
            'label' => Str::limit((string) $item->quote, 60),
        ]);

        $this->dispatch('toast', type: 'success', message: 'Exhibit added.');
    }

    public function toggleOption(string $key): void
    {
        $this->options[$key] = ! ($this->options[$key] ?? false);
    }

    public function setActiveSection(string $sectionKey): void
    {
        $this->activeSectionKey = $sectionKey;
    }

    public function requestValidation(): void
    {
        $this->validationRequested = true;
    }

    public function dismissGuidance(): void
    {
        $this->guidanceDismissed = true;
    }

    public function toggleAllEvidence(): void
    {
        $this->showAllEvidence = ! $this->showAllEvidence;
    }

    /**
     * Jump to the section that resolves the top blocker (or the first pending
     * suggestion for a review blocker), turning validation on so the offending
     * fields are highlighted.
     */
    public function goToBlocker(?string $sectionKey = null): void
    {
        $this->validationRequested = true;

        if ($sectionKey) {
            $this->activeSectionKey = $sectionKey;

            return;
        }

        // No explicit section (e.g. a review blocker) → jump to the first pending suggestion.
        $suggestion = $this->statement()->answers()->where('review_status', 'suggested')->first();
        if ($suggestion) {
            $this->activeSectionKey = $suggestion->section_key;
        }
    }

    /**
     * Move to the next section (config order) that still needs work.
     */
    public function goToNextIncompleteSection(): void
    {
        $progress = $this->service()->progress($this->statement());
        $sections = $this->questionnaire()->sections();
        $startIndex = $sections->search(fn ($s) => $s->key === $this->activeSectionKey);
        $ordered = $startIndex === false ? $sections : $sections->slice($startIndex + 1)->merge($sections->slice(0, $startIndex + 1));

        foreach ($ordered as $section) {
            $sp = $progress->sections[$section->key] ?? null;
            if ($sp && (($sp['required_unanswered'] ?? 0) > 0 || ($sp['awaiting_review'] ?? 0) > 0 || $sp['percent'] < 100)) {
                $this->activeSectionKey = $section->key;

                return;
            }
        }
    }

    public function generate(): void
    {
        $ws = $this->statement();
        $this->authorize('generate', $ws);

        $progress = $this->service()->progress($ws);
        if (! $progress->isReadyToGenerate()) {
            // Turn validation on and take the user to the top blocker.
            $blocker = $progress->primaryBlocker();
            $this->goToBlocker($blocker['section'] ?? null);
            $this->dispatch('toast', type: 'error', message: 'Resolve the outstanding issues before generating.');

            return;
        }

        $this->service()->generate($ws, $this->options);
        $this->dispatch('toast', type: 'success', message: 'Witness statement generated.');
    }

    // ── view data ────────────────────────────────────────────────────────────

    /**
     * The single data bundle every concept view renders from.
     *
     * @return array<string, mixed>
     */
    protected function witnessData(): array
    {
        if (! $this->statementId) {
            return [
                'hasStatement' => false,
                'questionnaire' => $this->questionnaire(),
            ];
        }

        $ws = $this->statement();
        $service = $this->service();
        $questionnaire = $this->questionnaire();

        $answerMeta = $ws->answers->keyBy(
            fn (WitnessStatementAnswer $a) => $a->section_key.'.'.$a->question_key
        );

        $evidence = $service->confirmedEvidence($ws);

        return [
            'hasStatement' => true,
            'statement' => $ws,
            'questionnaire' => $questionnaire,
            'groups' => $questionnaire->groups(),
            'progress' => $service->progress($ws),
            'answerMeta' => $answerMeta,
            'evidence' => $evidence,
            'contextualEvidence' => $this->showAllEvidence ? $evidence : $service->sectionEvidence($ws, $this->activeSectionKey),
            'exhibits' => $ws->exhibits->load('document'),
            'suggestions' => $ws->answers->where('review_status', 'suggested')->values(),
            'versions' => $ws->versions()->with('generatedBy')->get(),
            'optionsConfig' => $questionnaire->generationOptions(),
        ];
    }

    // ── internals ──────────────────────────────────────────────────────────────

    protected function service(): WitnessStatementService
    {
        return app(WitnessStatementService::class);
    }

    protected function questionnaire(): WitnessQuestionnaire
    {
        return app(WitnessQuestionnaire::class);
    }

    protected function statement(): WitnessStatement
    {
        return WitnessStatement::with(['answers', 'exhibits.document', 'client', 'claim'])
            ->findOrFail($this->statementId);
    }

    protected function answerFor(WitnessStatement $ws, int $answerId): WitnessStatementAnswer
    {
        return $ws->answers()->findOrFail($answerId);
    }

    protected function loadBuffers(WitnessStatement $ws): void
    {
        $this->answers = [];

        foreach ($ws->answers as $answer) {
            $this->answers[$answer->section_key][$answer->question_key] = $answer->value;
        }

        // Multiselect/repeater fields MUST be arrays or Livewire binds a group of
        // checkboxes to a single scalar and toggles them all together.
        foreach ($this->questionnaire()->sections() as $section) {
            foreach ($section->questions as $question) {
                if (in_array($question->type, ['multiselect', 'repeater'], true)) {
                    $current = $this->answers[$section->key][$question->key] ?? null;
                    if (! is_array($current)) {
                        $this->answers[$section->key][$question->key] = [];
                    }
                }
            }
        }

        $this->options = $ws->generation_options ?? [];
    }
}
