<?php

namespace App\Livewire\ApiLogs;

use App\Models\ApiRequestLog;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Products\LicenseToken;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
use Livewire\WithPagination;

class Index extends Component
{
    use WithPagination;

    public string $rangePreset = '30d'; // 24h, 7d, 30d, custom
    public ?string $dateFrom = null;
    public ?string $dateTo = null;

    public ?int $clientId = null;
    public ?int $environmentId = null;
    public bool $onlyWithoutEnvironment = false;
    public ?int $licenseTokenId = null;
    public ?string $action = null;
    public array $httpStatuses = [];
    public ?int $durationMin = null;
    public ?int $durationMax = null;
    public ?int $responseSizeMin = null;
    public ?int $responseSizeMax = null;

    public ?string $ip = null;
    public ?string $requestUuid = null;
    public ?string $host = null;

    protected $queryString = [
        'rangePreset' => ['except' => '30d'],
        'dateFrom',
        'dateTo',
        'clientId',
        'environmentId',
        'onlyWithoutEnvironment',
        'licenseTokenId',
        'action',
        'durationMin',
        'durationMax',
        'responseSizeMin',
        'responseSizeMax',
        'ip',
        'requestUuid',
        'host',
    ];

    public function mount(): void
    {
        $this->authorize('admin.api-logs.index');
        $this->applyDefaultRange();
    }

    protected function applyDefaultRange(): void
    {
        if ($this->rangePreset === 'custom' && $this->dateFrom && $this->dateTo) {
            return;
        }
        $end = Carbon::now();
        $start = match ($this->rangePreset) {
            '24h' => $end->copy()->subHours(24),
            '7d' => $end->copy()->subDays(7),
            '30d' => $end->copy()->subDays(30),
            default => $end->copy()->subDays(30),
        };
        $this->dateFrom = $start->format('Y-m-d\TH:i');
        $this->dateTo = $end->format('Y-m-d\TH:i');
    }

    #[Computed]
    public function appliedFiltersCounts(): array
    {
        $clienteEntorno = 0;
        if ($this->clientId !== null && $this->clientId !== '') {
            $clienteEntorno++;
        }
        if ($this->environmentId !== null && $this->environmentId !== '') {
            $clienteEntorno++;
        }
        if ($this->licenseTokenId !== null && $this->licenseTokenId !== '') {
            $clienteEntorno++;
        }
        if ($this->onlyWithoutEnvironment) {
            $clienteEntorno++;
        }
        if ($this->host !== null && $this->host !== '') {
            $clienteEntorno++;
        }

        $peticionRespuesta = 0;
        if ($this->action !== null && $this->action !== '') {
            $peticionRespuesta++;
        }
        if (! empty($this->httpStatuses)) {
            $peticionRespuesta++;
        }

        $avanzados = 0;
        if (is_string($this->ip) && trim($this->ip) !== '') {
            $avanzados++;
        }
        if (is_string($this->requestUuid) && trim($this->requestUuid) !== '') {
            $avanzados++;
        }
        if ($this->durationMin !== null) {
            $avanzados++;
        }
        if ($this->durationMax !== null) {
            $avanzados++;
        }
        if ($this->responseSizeMin !== null) {
            $avanzados++;
        }
        if ($this->responseSizeMax !== null) {
            $avanzados++;
        }

        return [
            'total' => $clienteEntorno + $peticionRespuesta + $avanzados,
            'cliente_entorno' => $clienteEntorno,
            'peticion_respuesta' => $peticionRespuesta,
            'avanzados' => $avanzados,
        ];
    }

    public function getDateRange(): array
    {
        $this->applyDefaultRange();
        $from = Carbon::parse($this->dateFrom);
        $to = Carbon::parse($this->dateTo);
        return [$from, $to];
    }

    public function getPreviousDateRange(): array
    {
        [$from, $to] = $this->getDateRange();
        $length = $from->diffInSeconds($to);
        $prevEnd = $from->copy()->subSecond();
        $prevStart = $prevEnd->copy()->subSeconds($length);
        return [$prevStart, $prevEnd];
    }

    protected function baseQuery()
    {
        [$from, $to] = $this->getDateRange();
        $q = ApiRequestLog::query()->whereBetween('started_at', [$from, $to]);

        if ($this->clientId) {
            $q->where('client_id', $this->clientId);
        }
        if ($this->environmentId) {
            $q->where('environment_id', $this->environmentId);
        }
        if ($this->onlyWithoutEnvironment) {
            $q->whereNull('environment_id');
        }
        if ($this->licenseTokenId) {
            $q->where('license_token_id', $this->licenseTokenId);
        }
        if ($this->action !== null && $this->action !== '') {
            $q->where('action', $this->action);
        }
        if ($this->httpStatuses !== []) {
            $q->whereIn('http_status', array_map('intval', $this->httpStatuses));
        }
        if ($this->durationMin !== null && $this->durationMin !== '') {
            $q->where('duration_ms', '>=', (int) $this->durationMin);
        }
        if ($this->durationMax !== null && $this->durationMax !== '') {
            $q->where('duration_ms', '<=', (int) $this->durationMax);
        }
        if ($this->responseSizeMin !== null && $this->responseSizeMin !== '') {
            $q->where('response_size', '>=', (int) $this->responseSizeMin);
        }
        if ($this->responseSizeMax !== null && $this->responseSizeMax !== '') {
            $q->where('response_size', '<=', (int) $this->responseSizeMax);
        }
        if ($this->ip !== null && $this->ip !== '') {
            $q->where('ip', 'like', '%' . $this->ip . '%');
        }
        if ($this->requestUuid !== null && $this->requestUuid !== '') {
            $q->where('request_uuid', 'like', '%' . $this->requestUuid . '%');
        }
        if ($this->host !== null && $this->host !== '') {
            $q->where('host', $this->host);
        }

        return $q;
    }

    public function resetFilters(): void
    {
        $this->rangePreset = '30d';
        $this->applyDefaultRange();
        $this->clientId = null;
        $this->environmentId = null;
        $this->licenseTokenId = null;
        $this->action = null;
        $this->onlyWithoutEnvironment = false;
        $this->httpStatuses = [];
        $this->durationMin = null;
        $this->durationMax = null;
        $this->responseSizeMin = null;
        $this->responseSizeMax = null;
        $this->ip = null;
        $this->requestUuid = null;
        $this->host = null;
        $this->resetPage();
    }

    public function openLogModal(int $logId): void
    {
        $this->dispatch('openModal',
            component: \App\Livewire\ApiLogs\LogDetailModal::class,
            arguments: ['logId' => $logId]
        );
    }

    /**
     * Filtra por el mismo sitio (si el log tiene entorno reconocido) o por el mismo host (si no).
     * - Sitio reconocido: aplica filtro "Sitio" (environmentId).
     * - Host no reconocido: aplica filtro por columna host (sin selector en filtros; se muestra como badge).
     */
    public function filterBySiteOrHost(?int $environmentId = null, ?string $host = null): void
    {
        if ($environmentId !== null && $environmentId !== '') {
            $this->environmentId = $environmentId;
            $this->host = null;
            $this->onlyWithoutEnvironment = false;
        } elseif ($host !== null && trim($host) !== '') {
            $this->host = trim($host);
            $this->environmentId = null;
            $this->onlyWithoutEnvironment = false;
        }
        $this->resetPage();
    }

    public function clearHostFilter(): void
    {
        $this->host = null;
        $this->resetPage();
    }

    #[On('api-logs-filter-by-site-or-host')]
    public function onFilterBySiteOrHost(?int $environmentId = null, ?string $host = null): void
    {
        $this->filterBySiteOrHost($environmentId, $host);
    }

    public function copyUuid(string $uuid): void
    {
        $this->dispatch('copy-to-clipboard', text: $uuid);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    public static function actionOptions(): array
    {
        return [
            'sync' => 'sync',
            'licence' => 'licence',
            'products' => 'products',
            'scss' => 'scss',
            'scss-cdn' => 'scss-cdn',
            'js' => 'js',
            'setup' => 'setup',
            'features' => 'features',
            'tutorials' => 'tutorials',
            'resources' => 'resources',
            'data' => 'data',
            'plugins' => 'plugins',
            'stats' => 'stats',
        ];
    }

    public function render()
    {
        $this->applyDefaultRange();
        [$from, $to] = $this->getDateRange();
        [$prevFrom, $prevTo] = $this->getPreviousDateRange();

        $base = $this->baseQuery();

        $total = (clone $base)->count();
        $count4xx = (clone $base)->whereBetween('http_status', [400, 499])->count();
        $count5xx = (clone $base)->where('http_status', '>=', 500)->count();
        $errorPct = $total > 0 ? round((($count4xx + $count5xx) / $total) * 100, 1) : 0;

        $prevBase = ApiRequestLog::query()->whereBetween('started_at', [$prevFrom, $prevTo]);
        if ($this->clientId) {
            $prevBase->where('client_id', $this->clientId);
        }
        if ($this->environmentId) {
            $prevBase->where('environment_id', $this->environmentId);
        }
        if ($this->licenseTokenId) {
            $prevBase->where('license_token_id', $this->licenseTokenId);
        }
        if ($this->action !== null && $this->action !== '') {
            $prevBase->where('action', $this->action);
        }
        $prevTotal = $prevBase->count();
        $prevCount4xx = (clone $prevBase)->whereBetween('http_status', [400, 499])->count();
        $prevCount5xx = (clone $prevBase)->where('http_status', '>=', 500)->count();
        $prevErrorPct = $prevTotal > 0 ? round((($prevCount4xx + $prevCount5xx) / $prevTotal) * 100, 1) : 0;

        $durations = (clone $base)->whereNotNull('duration_ms')->orderBy('duration_ms')->pluck('duration_ms')->values();
        $p50 = $this->percentile($durations, 50);
        $p95 = $this->percentile($durations, 95);
        $prevDurations = (clone $prevBase)->whereNotNull('duration_ms')->orderBy('duration_ms')->pluck('duration_ms')->values();
        $prevP50 = $this->percentile($prevDurations, 50);
        $prevP95 = $this->percentile($prevDurations, 95);

        $topActions = (clone $base)->select('action', DB::raw('count(*) as c'))->whereNotNull('action')->groupBy('action')->orderByDesc('c')->limit(3)->get();
        $topClients = (clone $base)->select('client_id', DB::raw('count(*) as c'))->whereNotNull('client_id')->groupBy('client_id')->orderByDesc('c')->limit(3)->get();
        foreach ($topClients as $row) {
            $row->client = Client::find($row->client_id);
        }

        $sitesErrorRate = (clone $base)
            ->select('environment_id', 'host', DB::raw('count(*) as total'), DB::raw('sum(case when http_status >= 400 then 1 else 0 end) as errors'))
            ->whereNotNull('environment_id')
            ->groupBy('environment_id', 'host')
            ->get()
            ->map(function ($row) {
                $row->error_pct = $row->total > 0 ? round(($row->errors / $row->total) * 100, 1) : 0;
                return $row;
            })
            ->sortByDesc('error_pct')
            ->take(3)
            ->values();

        foreach ($sitesErrorRate as $row) {
            $row->environment = Environment::find($row->environment_id);
        }

        $logs = (clone $base)->with(['client', 'site', 'licenseToken'])
            ->orderByDesc('started_at')
            ->paginate(20);

        return view('livewire.api-logs.index', [
            'logs' => $logs,
            'actionOptions' => self::actionOptions(),
            'total' => $total,
            'count4xx' => $count4xx,
            'count5xx' => $count5xx,
            'errorPct' => $errorPct,
            'prevTotal' => $prevTotal,
            'prevErrorPct' => $prevErrorPct,
            'p50' => $p50,
            'p95' => $p95,
            'prevP50' => $prevP50,
            'prevP95' => $prevP95,
            'topActions' => $topActions,
            'topClients' => $topClients,
            'sitesErrorRate' => $sitesErrorRate,
            'dateFrom' => $from,
            'dateTo' => $to,
        ])->layout('layouts.app');
    }

    protected function percentile($sortedValues, float $p): ?int
    {
        if ($sortedValues->isEmpty()) {
            return null;
        }
        $arr = $sortedValues->toArray();
        $idx = ($p / 100) * (count($arr) - 1);
        $lower = (int) floor($idx);
        $upper = (int) ceil($idx);
        if ($lower === $upper) {
            return $arr[$lower];
        }
        return (int) round($arr[$lower] + ($idx - $lower) * ($arr[$upper] - $arr[$lower]));
    }
}
