<?php

namespace App\Components\Report;

use App\Models\Claim\Claim;
use App\Models\Claim\Status;
use App\Models\Claim\StatusHistory;
use App\Models\Client\Client;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Url;
use Livewire\Component;

class TrendReport extends Component
{
    use AuthorizesRequests;

    #[Url]
    public int $weeks = 12;

    #[Url]
    public int $months = 12;

    public function mount(): void
    {
        $this->authorize('reports.trends');
    }

    public function render()
    {
        $statuses = Status::whereIn('display_name', [
            'Sent To Redress',
            'APP Number Received',
            'Investigation - DSAR Activity',
            'Investigation - DSARs Sent',
            'Part Dsar Received',
            'Closed',
        ])->get(['id', 'display_name']);

        $statusIds = $statuses->pluck('id', 'display_name');

        return view('components.report.trend-report', [
            'weeklyData'  => $this->buildWeeklyData($statusIds),
            'monthlyData' => $this->buildMonthlyData($statusIds),
        ]);
    }

    private function buildWeeklyData($statusIds): array
    {
        $weekCount = max(4, min($this->weeks, 52));
        $from      = now()->subWeeks($weekCount - 1)->startOfWeek();
        $to        = now()->endOfWeek();

        $scaffold = [];
        $labels   = [];
        $cursor   = $from->copy();
        while ($cursor->lte($to)) {
            $key            = $cursor->format('o-W');
            $scaffold[$key] = 0;
            $labels[$key]   = 'W' . $cursor->format('W') . ' \'' . $cursor->format('y');
            $cursor->addWeek();
        }

        $metrics = [];

        $raw = Client::whereBetween('created_at', [$from, $to])
            ->selectRaw("DATE_FORMAT(created_at, '%x-%v') as period, COUNT(*) as cnt")
            ->groupBy('period')
            ->pluck('cnt', 'period');

        $metrics['Clients Created'] = $this->mergeIntoScaffold($scaffold, $raw);

        foreach ($statusIds as $name => $id) {
            $raw = StatusHistory::where('status_histories.status_id', $id)
                ->where('status_histories.subject_type', Claim::class)
                ->whereBetween('status_histories.started_at', [$from, $to])
                ->join('claims', 'claims.id', '=', 'status_histories.subject_id')
                ->selectRaw("DATE_FORMAT(status_histories.started_at, '%x-%v') as period, COUNT(DISTINCT claims.client_id) as cnt")
                ->groupBy('period')
                ->pluck('cnt', 'period');

            $metrics[$name] = $this->mergeIntoScaffold($scaffold, $raw);
        }

        return [
            'labels'  => array_values($labels),
            'metrics' => $metrics,
        ];
    }

    private function buildMonthlyData($statusIds): array
    {
        $monthCount = max(3, min($this->months, 24));
        $from       = now()->subMonths($monthCount - 1)->startOfMonth();
        $to         = now()->endOfMonth();

        $scaffold = [];
        $labels   = [];
        $cursor   = $from->copy();
        while ($cursor->lte($to)) {
            $key            = $cursor->format('Y-m');
            $scaffold[$key] = 0;
            $labels[$key]   = $cursor->format('M \'y');
            $cursor->addMonth();
        }

        $metrics = [];

        $raw = Client::whereBetween('created_at', [$from, $to])
            ->selectRaw("DATE_FORMAT(created_at, '%Y-%m') as period, COUNT(*) as cnt")
            ->groupBy('period')
            ->pluck('cnt', 'period');

        $metrics['Clients Created'] = $this->mergeIntoScaffold($scaffold, $raw);

        foreach ($statusIds as $name => $id) {
            $raw = StatusHistory::where('status_histories.status_id', $id)
                ->where('status_histories.subject_type', Claim::class)
                ->whereBetween('status_histories.started_at', [$from, $to])
                ->join('claims', 'claims.id', '=', 'status_histories.subject_id')
                ->selectRaw("DATE_FORMAT(status_histories.started_at, '%Y-%m') as period, COUNT(DISTINCT claims.client_id) as cnt")
                ->groupBy('period')
                ->pluck('cnt', 'period');

            $metrics[$name] = $this->mergeIntoScaffold($scaffold, $raw);
        }

        return [
            'labels'  => array_values($labels),
            'metrics' => $metrics,
        ];
    }

    private function mergeIntoScaffold(array $scaffold, $raw): array
    {
        $result = $scaffold;
        foreach ($raw as $period => $count) {
            if (array_key_exists((string) $period, $result)) {
                $result[(string) $period] = (int) $count;
            }
        }
        return array_values($result);
    }
}
