<?php

namespace App\Livewire\Memory;

use App\Domain\Agent\Models\Agent;
use App\Domain\Memory\Actions\ResolveMemoryConflictAction;
use App\Domain\Memory\Enums\MemoryBeliefStatus;
use App\Domain\Memory\Enums\MemoryBeliefType;
use App\Domain\Memory\Enums\MemoryTier;
use App\Domain\Memory\Models\Memory;
use App\Domain\Project\Models\Project;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

class MemoryBrowserPage extends Component
{
    use WithPagination;

    #[Url]
    public string $search = '';

    #[Url]
    public string $agentFilter = '';

    #[Url]
    public string $projectFilter = '';

    #[Url]
    public string $sourceTypeFilter = '';

    /** Filter by memory tier. Empty string means all tiers. */
    #[Url]
    public string $tierFilter = '';

    /** Filter by tag. Empty string means all tags. */
    #[Url]
    public string $tagFilter = '';

    /** Filter by structured belief type. Empty string means all types. */
    #[Url]
    public string $beliefTypeFilter = '';

    /** Filter by belief lifecycle status. Empty string means all statuses. */
    #[Url]
    public string $beliefStatusFilter = '';

    /** Filter by retrieval domain scope. Empty string means all domains. */
    #[Url]
    public string $domainFilter = '';

    /** When '1', show only memories flagged as contradicting another belief. */
    #[Url]
    public string $conflictFilter = '';

    public string $sortField = 'created_at';

    public string $sortDirection = 'desc';

    public ?string $expandedId = null;

    private const ALLOWED_SORT_FIELDS = ['created_at', 'updated_at', 'tier', 'confidence', 'importance'];

    public function sortBy(string $field): void
    {
        if (! in_array($field, self::ALLOWED_SORT_FIELDS, true)) {
            return;
        }

        if ($this->sortField === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortField = $field;
            $this->sortDirection = 'asc';
        }
    }

    public function updatedSearch(): void
    {
        $this->resetPage();
    }

    public function updatedAgentFilter(): void
    {
        $this->resetPage();
    }

    public function updatedProjectFilter(): void
    {
        $this->resetPage();
    }

    public function updatedSourceTypeFilter(): void
    {
        $this->resetPage();
    }

    public function updatedTierFilter(): void
    {
        $this->resetPage();
    }

    public function updatedTagFilter(): void
    {
        $this->resetPage();
    }

    public function updatedBeliefTypeFilter(): void
    {
        $this->resetPage();
    }

    public function updatedBeliefStatusFilter(): void
    {
        $this->resetPage();
    }

    public function updatedDomainFilter(): void
    {
        $this->resetPage();
    }

    public function updatedConflictFilter(): void
    {
        $this->resetPage();
    }

    /**
     * Resolve a flagged contradiction: "supersede" marks the conflicting
     * partner superseded; "dismiss" clears the flag as a false positive.
     */
    public function resolveConflict(string $memoryId, string $resolution): void
    {
        Gate::authorize('edit-content');

        try {
            app(ResolveMemoryConflictAction::class)->execute(
                memoryId: $memoryId,
                teamId: (string) auth()->user()->current_team_id,
                resolution: $resolution,
            );
        } catch (\InvalidArgumentException $e) {
            session()->flash('error', $e->getMessage());

            return;
        }

        session()->flash('message', 'Conflict resolved.');
    }

    /**
     * Replace all tags on a memory. Accepts a comma-separated string.
     */
    public function updateTags(string $memoryId, string $tagsString): void
    {
        Gate::authorize('edit-content');

        $tags = array_values(array_unique(array_filter(
            array_map('trim', explode(',', $tagsString)),
        )));

        // Validate tag format: only lowercase alphanumeric, colons, hyphens, underscores
        foreach ($tags as $tag) {
            if (! preg_match('/^[a-z0-9:_-]{1,50}$/', $tag)) {
                session()->flash('error', "Invalid tag format: \"{$tag}\". Use lowercase letters, numbers, colons, hyphens, underscores (max 50 chars).");

                return;
            }
        }

        Memory::where('id', $memoryId)->update(['tags' => $tags]);

        session()->flash('message', 'Tags updated.');
    }

    public function toggleExpand(string $id): void
    {
        $this->expandedId = $this->expandedId === $id ? null : $id;
    }

    public function deleteMemory(string $id): void
    {
        Gate::authorize('edit-content');

        Memory::where('id', $id)->delete();

        if ($this->expandedId === $id) {
            $this->expandedId = null;
        }

        session()->flash('message', 'Memory deleted.');
    }

    /**
     * Promote a memory to the given target tier.
     * Requires the edit-content gate.
     */
    public function promoteTier(string $memoryId, string $targetTier): void
    {
        Gate::authorize('edit-content');

        $tier = MemoryTier::tryFrom($targetTier);
        if (! $tier) {
            session()->flash('error', 'Invalid target tier.');

            return;
        }

        Memory::where('id', $memoryId)->update([
            'tier' => $tier->value,
            'proposal_status' => 'approved',
            'reviewed_at' => now(),
            'reviewed_by' => 'user:'.(auth()->user()?->email ?? 'anonymous'),
        ]);

        session()->flash('message', "Memory promoted to {$tier->value}.");
    }

    /**
     * Reject a Proposed-tier memory. Records the reason and stops it from
     * surfacing in retrieval. Used to dismiss noisy or duplicate proposals.
     */
    public function rejectProposal(string $memoryId, string $reason): void
    {
        Gate::authorize('edit-content');

        $reason = trim($reason);
        if ($reason === '') {
            session()->flash('error', 'A rejection reason is required.');

            return;
        }

        Memory::where('id', $memoryId)->update([
            'proposal_status' => 'rejected',
            'reviewed_at' => now(),
            'rejection_reason' => mb_substr($reason, 0, 1000),
            'reviewed_by' => 'user:'.(auth()->user()?->email ?? 'anonymous'),
        ]);

        if ($this->expandedId === $memoryId) {
            $this->expandedId = null;
        }

        session()->flash('message', 'Proposal rejected.');
    }

    public function render(): View
    {
        $query = Memory::query()->with(['agent', 'project', 'supersedes', 'conflictsWith']);

        if ($this->search) {
            $query->where('content', 'ilike', "%{$this->search}%");
        }

        if ($this->agentFilter) {
            $query->where('agent_id', $this->agentFilter);
        }

        if ($this->projectFilter) {
            $query->where('project_id', $this->projectFilter);
        }

        if ($this->sourceTypeFilter) {
            $query->where('source_type', $this->sourceTypeFilter);
        }

        if ($this->tierFilter) {
            $query->where('tier', $this->tierFilter);
        }

        if ($this->beliefTypeFilter) {
            $query->where('belief_type', $this->beliefTypeFilter);
        }

        if ($this->beliefStatusFilter) {
            $query->where('belief_status', $this->beliefStatusFilter);
        }

        if ($this->domainFilter) {
            $query->where('domain', $this->domainFilter);
        }

        if ($this->conflictFilter === '1') {
            $query->where('conflict_flag', true);
        }

        if ($this->tagFilter) {
            if (config('database.default') === 'pgsql') {
                $query->whereRaw('tags @> ?', [json_encode([$this->tagFilter])]);
            } else {
                $query->whereRaw(
                    'EXISTS (SELECT 1 FROM json_each(tags) WHERE json_each.value = ?)',
                    [$this->tagFilter],
                );
            }
        }

        $query->orderBy($this->sortField, $this->sortDirection);

        // Count unreviewed proposed memories for the badge
        $proposalCount = Memory::query()
            ->where('tier', MemoryTier::Proposed->value)
            ->count();

        // Count memories flagged as contradicting another belief.
        $conflictCount = Memory::query()
            ->where('conflict_flag', true)
            ->count();

        // Collect distinct tags across the current team's memories. The
        // explicit team_id bind keeps tag names from leaking across tenants
        // (the memories table has no row-level security; this query bypasses
        // TeamScope by going through the DB facade, so the filter is mandatory).
        $availableTags = collect();
        if (config('database.default') === 'pgsql') {
            $teamId = auth()->user()?->current_team_id;
            if ($teamId !== null) {
                $availableTags = collect(DB::select(
                    "SELECT DISTINCT jsonb_array_elements_text(tags) AS tag
                     FROM memories
                     WHERE team_id = ?
                       AND tags IS NOT NULL
                       AND tags != '[]'::jsonb
                     ORDER BY tag",
                    [$teamId],
                ))->pluck('tag');
            }
        }

        return view('livewire.memory.memory-browser-page', [
            'memories' => $query->paginate(30),
            'agents' => Agent::orderBy('name')->pluck('name', 'id'),
            'projects' => Project::orderBy('title')->pluck('title', 'id'),
            'sourceTypes' => Memory::distinct()->pluck('source_type')->sort()->values(),
            'tiers' => MemoryTier::cases(),
            'beliefTypes' => MemoryBeliefType::cases(),
            'beliefStatuses' => MemoryBeliefStatus::cases(),
            'domains' => Memory::query()->whereNotNull('domain')->distinct()->orderBy('domain')->pluck('domain'),
            'proposalCount' => $proposalCount,
            'conflictCount' => $conflictCount,
            'availableTags' => $availableTags,
        ])->layout('layouts.app', ['header' => 'Memory Browser']);
    }
}
