<?php

use App\Models\QrCode;
use App\Models\QrCodeScan;
use Flux\Flux;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

return new class extends Component {
    use WithPagination;

    public QrCode $qrCode;

    #[Url(as: 'range')]
    public string $range = '30';

    #[Url(as: 'humans')]
    public bool $humansOnly = true;

    public function mount(QrCode $qrCode): void
    {
        $this->qrCode = $qrCode;
    }

    /**
     * Reset pagination whenever the date range changes.
     */
    public function updatingRange(): void
    {
        $this->resetPage();
    }

    /**
     * Reset pagination whenever the humans-only toggle changes.
     */
    public function updatingHumansOnly(): void
    {
        $this->resetPage();
    }

    /**
     * Toggle this QR code between active and inactive.
     */
    public function toggleActive(): void
    {
        $this->qrCode->update(['is_active' => ! $this->qrCode->is_active]);

        Flux::toast(
            variant: 'success',
            text: $this->qrCode->is_active ? __('QR code activated.') : __('QR code deactivated.'),
        );
    }

    /**
     * @return array<string, int>
     */
    #[Computed]
    public function stats(): array
    {
        $scans = $this->scansQuery();

        return [
            'total' => (clone $scans)->count(),
            'unique_visitors' => (clone $scans)->distinct()->count('visitor_hash'),
            'today' => (clone $scans)->whereDate('scanned_at', now())->count(),
            'times_generated' => $this->qrCode->renders()
                ->when($this->rangeStart() !== null, fn ($query) => $query->where('created_at', '>=', $this->rangeStart()))
                ->count(),
        ];
    }

    /**
     * Bot/crawler scans within the current date range, shown alongside the
     * humans-only toggle regardless of its state so the number stays
     * explainable rather than silently dropped.
     */
    #[Computed]
    public function botScanCount(): int
    {
        return QrCodeScan::query()
            ->where('qr_code_id', $this->qrCode->id)
            ->where('is_bot', true)
            ->when($this->rangeStart() !== null, fn ($query) => $query->where('scanned_at', '>=', $this->rangeStart()))
            ->count();
    }

    /**
     * Scans that haven't been picked up by the enrichment job yet, within
     * the current filters. Excluded from the device/browser/geo breakdowns
     * below since their values are simply unknown, not "Unknown".
     */
    #[Computed]
    public function pendingEnrichmentCount(): int
    {
        return $this->scansQuery()->whereNull('enriched_at')->count();
    }

    /**
     * @return Collection<int, object{day: string, total: int}>
     */
    #[Computed]
    public function scansByDay(): Collection
    {
        return $this->scansQuery()
            ->selectRaw('DATE(scanned_at) as day, COUNT(*) as total')
            ->groupBy('day')
            ->orderBy('day')
            ->get();
    }

    /**
     * @return Collection<int, object{device_type: string|null, total: int}>
     */
    #[Computed]
    public function byDeviceType(): Collection
    {
        return $this->enrichedBreakdown('device_type');
    }

    /**
     * @return Collection<int, object{browser: string|null, total: int}>
     */
    #[Computed]
    public function byBrowser(): Collection
    {
        return $this->enrichedBreakdown('browser');
    }

    /**
     * @return Collection<int, object{platform: string|null, total: int}>
     */
    #[Computed]
    public function byPlatform(): Collection
    {
        return $this->enrichedBreakdown('platform');
    }

    /**
     * @return Collection<int, object{country: string|null, total: int}>
     */
    #[Computed]
    public function byCountry(): Collection
    {
        return $this->enrichedBreakdown('country');
    }

    /**
     * @return Collection<int, object{city: string|null, total: int}>
     */
    #[Computed]
    public function byCity(): Collection
    {
        return $this->enrichedBreakdown('city');
    }

    /**
     * @return Collection<int, object{referer: string, total: int}>
     */
    #[Computed]
    public function topReferrers(): Collection
    {
        return $this->scansQuery()
            ->whereNotNull('referer')
            ->selectRaw('referer, COUNT(*) as total')
            ->groupBy('referer')
            ->orderByDesc('total')
            ->limit(10)
            ->get();
    }

    /**
     * Scans in the current filters with no referrer at all (QR scans
     * typically have none, since the camera app opens the link directly).
     */
    #[Computed]
    public function directScanCount(): int
    {
        return $this->scansQuery()->whereNull('referer')->count();
    }

    /**
     * @return LengthAwarePaginator<int, QrCodeScan>
     */
    #[Computed]
    public function recentScans(): LengthAwarePaginator
    {
        return $this->scansQuery()
            ->orderByDesc('scanned_at')
            ->paginate(25);
    }

    /**
     * Render "Pending enrichment" for a value that hasn't been enriched yet,
     * instead of a misleading blank cell.
     */
    public function pendingOrValue(?CarbonInterface $enrichedAt, ?string $value): string
    {
        if ($enrichedAt === null) {
            return __('Pending enrichment');
        }

        return $value ?? '-';
    }

    /**
     * Base query for scans against this QR code, with the humans-only
     * toggle and date range filter applied.
     *
     * @return Builder<QrCodeScan>
     */
    private function scansQuery(): Builder
    {
        return QrCodeScan::query()
            ->where('qr_code_id', $this->qrCode->id)
            ->when($this->humansOnly, fn ($query) => $query->humans())
            ->when($this->rangeStart() !== null, fn ($query) => $query->where('scanned_at', '>=', $this->rangeStart()));
    }

    /**
     * Group scans by the given column, limited to already-enriched scans so
     * "no value" reliably means "no data available" (e.g. a private/local
     * IP) rather than "not enriched yet".
     *
     * @return Collection<int, object>
     */
    private function enrichedBreakdown(string $column): Collection
    {
        return $this->scansQuery()
            ->whereNotNull('enriched_at')
            ->selectRaw("{$column}, COUNT(*) as total")
            ->groupBy($column)
            ->orderByDesc('total')
            ->limit(10)
            ->get();
    }

    /**
     * The lower bound of the current date range, or null for "All time".
     */
    private function rangeStart(): ?CarbonInterface
    {
        return match ($this->range) {
            '7' => now()->subDays(7)->startOfDay(),
            '90' => now()->subDays(90)->startOfDay(),
            'all' => null,
            default => now()->subDays(30)->startOfDay(),
        };
    }

    protected function view($data = [])
    {
        return app('view')->file('/Users/dineshmali/code/pushpvatikaflorist1/storage/framework/views/livewire/views/d322302f.blade.php', $data);
    }
}; 