<?php

declare(strict_types=1);

namespace App\Filament\Widgets;

use App\Models\Job;
use App\Models\OpsTask;
use Carbon\Carbon;
use Filament\Widgets\Widget;

class OpsTodayWidget extends Widget
{
    protected static string $view = 'filament.widgets.ops-today-widget';

    public function getColumnSpan(): int|array|string
    {
        return [
            'default' => 12,
            'md'      => 12,
            'lg'      => 12,
            'xl'      => 12,
        ];
    }

    public int $pickupsCount = 0;
    public int $returnsCount = 0;
    public int $tasksCount   = 0;

    /** @var \Illuminate\Support\Collection<int,\App\Models\Job> */
    public $pickups;

    /** @var \Illuminate\Support\Collection<int,\App\Models\Job> */
    public $returns;

    /** @var \Illuminate\Support\Collection<int,\App\Models\OpsTask> */
    public $tasks;

    public function mount(): void
    {
        $this->refreshData();
    }

    /**
     * Apply BrandContext scoping when brand_id exists.
     */
    protected function applyBrandScope($query)
    {
        if (class_exists(\App\Support\BrandContext::class)) {
            $brandId = \App\Support\BrandContext::currentBrandId();

            if ($brandId) {
                $query->where('brand_id', $brandId);
            }
        }

        return $query;
    }

    public function refreshData(): void
    {
        $today = Carbon::today();

        /*
         * PICKUPS TODAY
         * Use the same pattern as the OpsToday page:
         * - brand scope
         * - eager-load vehicle
         * - clean UTF-8 on customer & vehicle fields
         */
        $this->pickups = $this->applyBrandScope(
            Job::query()
                ->whereNotNull('start_at')
                ->whereDate('start_at', $today)
                ->with('vehicle')
                ->orderBy('start_at')
        )
            ->get()
            ->map(fn (Job $job) => $this->cleanJobForDashboard($job));

        $this->pickupsCount = $this->pickups->count();

        /*
         * RETURNS TODAY
         */
        $this->returns = $this->applyBrandScope(
            Job::query()
                ->whereNotNull('end_at')
                ->whereDate('end_at', $today)
                ->with('vehicle')
                ->orderBy('end_at')
        )
            ->get()
            ->map(fn (Job $job) => $this->cleanJobForDashboard($job));

        $this->returnsCount = $this->returns->count();

        /*
         * TASKS (DUE / OVERDUE) — keep it simple, but eager-load vehicle.
         */
        $tasksQuery = OpsTask::query()
            ->with('vehicle')
            ->forCurrentBrand()
            ->open()
            ->dueTodayOrOverdue()
            ->orderBy('priority')
            ->orderBy('due_date');

        $this->tasksCount = (clone $tasksQuery)->count();
        $this->tasks      = $tasksQuery->limit(3)->get();
    }

    /**
     * Mirror the cleaning logic from OpsToday so Livewire never chokes
     * on invalid UTF-8 coming from external systems (VEVS etc).
     */
    protected function cleanJobForDashboard(Job $job): Job
    {
        $job->customer_name = $this->cleanUtf8($job->customer_name);

        if ($job->vehicle) {
            $job->vehicle->name         = $this->cleanUtf8($job->vehicle->name);
            $job->vehicle->display_name = $this->cleanUtf8($job->vehicle->display_name ?? '');
            $job->vehicle->registration = $this->cleanUtf8($job->vehicle->registration ?? '');
        }

        return $job;
    }

    /**
     * Strip invalid UTF-8 bytes so Livewire / json_encode don’t explode.
     */
    protected function cleanUtf8(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }

        return iconv('UTF-8', 'UTF-8//IGNORE', $value) ?: $value;
    }
}
