<?php

namespace App\Components\Report;

use App\Enum\Dsar\DsarStatus;
use App\Models\Claim\Claim;
use App\Models\Dsar\Dsar;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
use Livewire\WithPagination;

/**
 * DSAR Report by Client.
 *
 * Lists every client that has DSARs and shows how many have been "returned"
 * (terminal status — see DsarStatus::terminal()). A client is "ready to
 * progress" when ALL of their DSARs are terminal. A per-client breakdown of
 * the individual DSARs is shown in an offcanvas panel.
 *
 * One client has exactly one claim (Client::claim() is hasOne), so aggregating
 * DSARs by claim_id is equivalent to aggregating by client — this avoids a join
 * to claims while keeping the Dsar global solicitor scope intact.
 */
class DsarClientReport extends Component {
    use AuthorizesRequests;
    use WithPagination;

    public string $search = '';
    public bool $readyOnly = false;
    public string $sort = 'ready';

    // Per-client breakdown offcanvas state.
    public ?int $selectedClaimId = null;

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

    public function updatedSearch(): void {
        $this->resetPage('clientsPage');
    }

    public function updatedReadyOnly(): void {
        $this->resetPage('clientsPage');
    }

    public function updatedSort(): void {
        $this->resetPage('clientsPage');
    }

    /** Open the breakdown offcanvas for a given claim/client. */
    public function showBreakdown(int $claimId): void {
        $this->selectedClaimId = $claimId;
        $this->dispatch('open-dsar-breakdown'); // Alpine opens the offcanvas on this event
    }

    public function render() {
        $rows = $this->buildClientBreakdown();

        // Summary stats — computed from the full collection (before pagination).
        $statTotal       = $rows->count();
        $statReady       = $rows->where('ready', true)->count();
        $statOutstanding = $rows->where('outstanding', '>', 0)->count();

        // Search + ready-only filters.
        if ($this->search !== '') {
            $term = strtolower($this->search);
            $rows = $rows->filter(function ($row) use ($term) {
                return str_contains(strtolower($row->client->fullName), $term)
                    || str_contains(strtolower((string) $row->client->reference), $term);
            });
        }

        if ($this->readyOnly) {
            $rows = $rows->where('ready', true);
        }

        // Sort.
        $rows = match ($this->sort) {
            'outstanding' => $rows->sortByDesc('outstanding'),
            'progress'    => $rows->sortByDesc('pct'),
            'name'        => $rows->sortBy(fn ($r) => strtolower($r->client->fullName)),
            'oldest'      => $rows->sortByDesc(fn ($r) => $r->oldest_age ?? -1),
            default       => $rows->sortBy([            // 'ready' — ready first, then most outstanding
                ['ready', 'desc'],
                ['outstanding', 'desc'],
            ]),
        };
        $rows = $rows->values();

        // Paginate the resulting collection (manual paginator — same pattern as
        // App\Components\Claim\Dsars).
        $perPage = 25;
        $page    = LengthAwarePaginator::resolveCurrentPage('clientsPage');
        $items   = $rows->slice(($page - 1) * $perPage, $perPage)->values();
        $paginated = new LengthAwarePaginator(
            $items,
            $rows->count(),
            $perPage,
            $page,
            [
                'path'     => request()->url(),
                'query'    => request()->query(),
                'pageName' => 'clientsPage',
            ]
        );

        // Selected client's DSARs for the breakdown offcanvas.
        $selectedClient = null;
        $selectedDsars  = collect();
        if ($this->selectedClaimId) {
            $claim = Claim::with(['client', 'dsars' => fn ($q) =>
                        $q->whereNotIn('status', DsarStatus::preSend())
                          ->with('institution.dsarType')->orderBy('institution_id')])
                ->find($this->selectedClaimId);
            $selectedClient = $claim?->client;
            $selectedDsars  = $claim?->dsars ?? collect();
        }

        return view('components.dsar.report-client', [
            'rows'            => $paginated,
            'statTotal'       => $statTotal,
            'statReady'       => $statReady,
            'statOutstanding' => $statOutstanding,
            'selectedClient'  => $selectedClient,
            'selectedDsars'   => $selectedDsars,
        ]);
    }

    /**
     * One row per client (via claim_id) with returned/outstanding/etc. counts.
     */
    protected function buildClientBreakdown() {
        $terminalIn    = "'" . implode("','", DsarStatus::terminal()) . "'";
        $outstandingIn = "'" . implode("','", DsarStatus::outstanding()) . "'";

        $totals = Dsar::query()
            ->whereNotIn('status', DsarStatus::preSend())   // report counts sent DSARs only (excludes draft/approved)
            ->select(
                'claim_id',
                DB::raw('COUNT(*) as total'),
                DB::raw("SUM(CASE WHEN status IN ({$terminalIn}) THEN 1 ELSE 0 END) as returned"),
                DB::raw("SUM(CASE WHEN status IN ({$outstandingIn}) THEN 1 ELSE 0 END) as outstanding"),
                DB::raw("SUM(CASE WHEN status = 'acknowledged' THEN 1 ELSE 0 END) as acknowledged"),
                DB::raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
                DB::raw("MIN(CASE WHEN status IN ({$outstandingIn}) THEN created_at ELSE NULL END) as oldest_outstanding"),
            )
            ->groupBy('claim_id')
            ->get();

        $claims = Claim::with('client')
            ->whereIn('id', $totals->pluck('claim_id'))
            ->get()
            ->keyBy('id');

        return $totals->map(function ($r) use ($claims) {
            $client = $claims->get($r->claim_id)?->client;
            if (!$client) {
                return null;                            // skip orphaned/clientless claims
            }

            $total    = (int) $r->total;
            $returned = (int) $r->returned;

            return (object) [
                'client'      => $client,
                'claim_id'    => (int) $r->claim_id,
                'total'       => $total,
                'returned'    => $returned,
                'outstanding'  => (int) $r->outstanding,
                'acknowledged' => (int) $r->acknowledged,
                'failed'      => (int) $r->failed,
                'ready'       => $total > 0 && $returned === $total,
                'pct'         => $total > 0 ? (int) round(($returned / $total) * 100) : 0,
                // Carbon 3's diffInDays is signed and oldest_outstanding is in the
                // past, so abs() keeps the age positive (and 'oldest' sorting right).
                'oldest_age'  => $r->oldest_outstanding ? (int) abs(now()->diffInDays($r->oldest_outstanding)) : null,
            ];
        })->filter()->values();
    }
}
