<?php

namespace App\Livewire;

use App\Domain\Financial\Models\Invoice;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\TrainingGroup;
use Livewire\Component;

class GlobalSearch extends Component
{
    use UsesBranchScope;

    public string $query = '';
    public bool $showResults = false;
    public array $results = [];

    public function updatedQuery(): void
    {
        if (strlen($this->query) < 2) {
            $this->results = [];
            $this->showResults = false;
            return;
        }

        $this->search();
    }

    public function search(): void
    {
        $q = $this->query;
        $branchId = $this->getActiveBranchId();
        $results = [];

        $participants = Participant::with('person')
            ->when($branchId, fn ($query) => $query->where('branch_id', $branchId))
            ->where(function ($query) use ($q) {
                $query->where('participant_number', 'ilike', "%{$q}%")
                    ->orWhere('membership_id', 'ilike', "%{$q}%")
                    ->orWhereHas('person', function ($pq) use ($q) {
                        $pq->where('name_ar', 'ilike', "%{$q}%")
                            ->orWhere('name', 'ilike', "%{$q}%")
                            ->orWhere('phone', 'like', "%{$q}%");
                    });
            })
            ->limit(5)
            ->get();

        foreach ($participants as $p) {
            $results[] = [
                'type' => 'participant',
                'label' => $p->person?->name_ar ?? $p->person?->name ?? $p->participant_number,
                'subtitle' => $p->participant_number . ' — ' . ($p->status?->value ?? ''),
                'url' => route('participants.show', $p),
            ];
        }

        $invoices = Invoice::with('billable')
            ->where(function ($query) use ($q) {
                $query->where('number', 'ilike', "%{$q}%")
                    ->orWhere('contact_name', 'ilike', "%{$q}%");
            })
            ->limit(3)
            ->get();

        foreach ($invoices as $inv) {
            $results[] = [
                'type' => 'invoice',
                'label' => $inv->number,
                'subtitle' => ($inv->contact_name ?? $inv->billable?->person?->name_ar ?? '') . ' — ' . number_format($inv->total_amount / 100, 2) . ' ج.م',
                'url' => route('invoices.show', $inv),
            ];
        }

        $groups = TrainingGroup::with('program')
            ->when($branchId, fn ($query) => $query->where('branch_id', $branchId))
            ->where(function ($query) use ($q) {
                $query->where('name_ar', 'ilike', "%{$q}%")
                    ->orWhere('name', 'ilike', "%{$q}%");
            })
            ->limit(3)
            ->get();

        foreach ($groups as $g) {
            $results[] = [
                'type' => 'group',
                'label' => $g->name_ar ?? $g->name,
                'subtitle' => ($g->program?->name_ar ?? '') . " ({$g->current_count}/{$g->max_capacity})",
                'url' => route('groups.edit', $g),
            ];
        }

        $this->results = $results;
        $this->showResults = !empty($results);
    }

    public function selectResult(string $url): void
    {
        // The url arrives from the browser, so it is a destination request, not
        // a destination. Every url this component produces is route()-built and
        // therefore same-origin; anything else is someone else's idea of where
        // the user should go next, and sending them there under our own domain
        // is how a search box becomes a phishing hop.
        if (! $this->isInternalUrl($url)) {
            return;
        }

        $this->redirect($url, navigate: true);
    }

    /**
     * Same-origin, and a path rather than a protocol-relative or absolute url
     * pointing elsewhere.
     */
    private function isInternalUrl(string $url): bool
    {
        if ($url === '' || str_starts_with($url, '//') || str_contains($url, "\\")) {
            return false;
        }

        $host = parse_url($url, PHP_URL_HOST);

        if ($host === null || $host === false) {
            // A bare path. Require a leading slash so "javascript:alert(1)" and
            // friends — which parse to no host — cannot get through.
            return str_starts_with($url, '/');
        }

        return $host === parse_url(config('app.url'), PHP_URL_HOST)
            || $host === request()->getHost();
    }

    public function render()
    {
        return view('livewire.global-search');
    }
}
