<?php
namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\Attachment;
use App\Models\EmployeeDocument;
use App\Models\Equipment;
use App\Models\EquipmentDocument;
use App\Models\WorkPermit;
use Carbon\Carbon;
use Illuminate\Http\Request;

/** Live data for the Command Centre — Documents & Permits module (Group scope: registers span all sites). */
class DocsController extends Controller
{
    use Concerns\DashboardHelpers;

    public function show(Request $r)
    {
        $this->need('docs.view');
        [$cf, $ct, $pf, $pt, $grain] = $this->windows((string) $r->get('period', 'This Month'));
        $today = Carbon::today();

        $eqDocs = EquipmentDocument::all();
        $valid = $eqDocs->filter(fn($d) => !$d->expiry_date || Carbon::parse($d->expiry_date)->gt($today));
        $expSoon = $eqDocs->filter(fn($d) => $d->expiry_date && Carbon::parse($d->expiry_date)->between($today, $today->copy()->addDays(30)))->count();
        $expd = $eqDocs->filter(fn($d) => $d->expiry_date && Carbon::parse($d->expiry_date)->lt($today))->count();
        $current = $eqDocs->filter(fn($d) => !$d->expiry_date || Carbon::parse($d->expiry_date)->gt($today->copy()->addDays(30)))->count();

        $ptw = WorkPermit::whereBetween('permit_date', [$cf, $ct])->count();
        $ptwP = WorkPermit::whereBetween('permit_date', [$pf, $pt])->count();
        $pend = WorkPermit::where('status', 'pending')->count();
        $live = WorkPermit::where('status', 'approved')->where('valid_to', '>=', $today->format('Y-m-d') . ' 00:00:00')->count();
        $blast = WorkPermit::whereBetween('permit_date', [$cf, $ct])->where('work_type', 'like', '%blast%')->count();
        $blastP = WorkPermit::whereBetween('permit_date', [$pf, $pt])->where('work_type', 'like', '%blast%')->count();

        $eqTot = Equipment::count();
        $covIds = $valid->groupBy('equipment_id')->filter(fn($g) =>
            $g->contains(fn($d) => stripos((string) $d->doc_type, 'insur') !== false)
            && $g->contains(fn($d) => stripos((string) $d->doc_type, 'road') !== false))->keys()->count();
        $cover = $eqTot > 0 ? round($covIds / $eqTot * 100, 1) : 0;

        $total = $eqDocs->count() + WorkPermit::count() + Attachment::count() + EmployeeDocument::count();
        $ins = $eqDocs->filter(fn($d) => stripos((string) $d->doc_type, 'insur') !== false)->count();
        $road = $eqDocs->filter(fn($d) => stripos((string) $d->doc_type, 'road') !== false)->count();

        $cards = [
            $this->card('Docs on File', $total, '#', $total, [], ['st' => 'blue']),
            $this->card('Expiring ≤ 30 Days', $expSoon, '#', $expSoon, [], ['good' => 'down', 'st' => $expSoon > 0 ? 'red' : 'green']),
            $this->card('Expired', $expd, '#', $expd, [], ['good' => 'down', 'st' => $expd > 0 ? 'red' : 'green']),
            $this->card('Valid & Current', $current, '#', $current, [], ['st' => 'green']),
            $this->card('Work Permits', $ptw, '#', $ptwP),
            $this->card('Permits Pending', $pend, '#', $pend, [], ['good' => 'down', 'st' => $pend > 0 ? 'amber' : 'green']),
            $this->card('Permits Live', $live, '#', $live, [], ['st' => 'blue']),
            $this->card('Blasting Permits', $blast, '#', $blastP),
            $this->card('Fleet Doc Cover', $cover, '%', $cover, [], ['tgt' => 100]),
            $this->card('Insurance Docs', $ins, '#', $ins, [], ['st' => 'blue']),
            $this->card('Roadworthy Docs', $road, '#', $road, [], ['st' => 'blue']),
            $this->card('Attachments Filed', Attachment::count(), '#', Attachment::count(), [], ['st' => 'blue']),
        ];

        return response()->json([
            'sub' => 'Registers · expiries · work permits · Group · ' . $r->get('period', 'This Month'),
            'asAt' => Carbon::today()->format('d M Y'),
            'cards' => $cards,
            'sections' => [
                $this->secExp(),
                $this->secPtw(),
                $this->secDtype(),
                $this->secEquip(),
                $this->secEqDoc($today),
                $this->secPtwTab(),
                $this->secGap($today),
            ],
        ]);
    }

    private function secExp(): array
    {
        $t = Carbon::today();
        $labels = [];
        $data = [];
        for ($i = 0; $i < 12; $i++) {
            $d = $t->copy()->addMonths($i);
            $labels[] = $d->format('M y');
            $data[] = EquipmentDocument::whereBetween('expiry_date', [$d->copy()->startOfMonth()->format('Y-m-d'), $d->copy()->endOfMonth()->format('Y-m-d')])->count();
        }
        return ['k' => 'chart', 'id' => 'dc_exp', 'ti' => 'Expiry Pipeline — equipment docs expiring per month (12 months)', 'cls' => 'wide', 'spec' => [
            'type' => 'bar', 'fmt' => 'num',
            'labels' => $labels,
            'series' => [['name' => 'Expiring', 'data' => $data, 'color' => '#ef4444']],
        ]];
    }

    private function secPtw(): array
    {
        $rows = WorkPermit::select('status', \DB::raw('count(*) n'))->groupBy('status')->get();
        $pal = $this->palette();
        $items = $rows->values()->map(fn($r, $i) => ['label' => ucfirst((string) $r->status), 'value' => (int) $r->n, 'color' => $pal[$i % count($pal)]])->all();
        return ['k' => 'chart', 'id' => 'dc_ptw', 'ti' => 'Work Permits by Status — count (live)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'num', 'center' => 'Permits', 'items' => $items,
        ]];
    }

    private function secDtype(): array
    {
        $pal = $this->palette();
        $items = [
            ['label' => 'Insurance', 'value' => EquipmentDocument::where('doc_type', 'like', '%insur%')->count(), 'color' => $pal[0]],
            ['label' => 'Roadworthy', 'value' => EquipmentDocument::where('doc_type', 'like', '%road%')->count(), 'color' => $pal[1]],
            ['label' => 'Work permits', 'value' => WorkPermit::count(), 'color' => $pal[2]],
            ['label' => 'Attachments', 'value' => Attachment::count(), 'color' => $pal[3]],
        ];
        $items = array_values(array_filter($items, fn($x) => $x['value'] > 0));
        return ['k' => 'chart', 'id' => 'dc_dtype', 'ti' => 'Docs by Type — count (live)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'num', 'center' => 'Docs', 'items' => $items,
        ]];
    }

    private function secEquip(): array
    {
        $today = Carbon::today();
        $map = EquipmentDocument::all()->groupBy('equipment_id')->map(fn($g) =>
            $g->filter(fn($d) => !$d->expiry_date || Carbon::parse($d->expiry_date)->gt($today))->count());
        $eq = Equipment::orderBy('code')->get(['id', 'code']);
        return ['k' => 'chart', 'id' => 'dc_equip', 'ti' => 'Equipment Doc Cover — valid docs per machine (live)', 'spec' => [
            'type' => 'hbar', 'fmt' => 'num',
            'labels' => $eq->map(fn($e) => $e->code)->all(),
            'series' => [['name' => 'Valid docs', 'data' => $eq->map(fn($e) => $map[$e->id] ?? 0)->all(), 'color' => '#22c55e']],
        ]];
    }

    private function secEqDoc($today): array
    {
        $rows = EquipmentDocument::with('equipment')->orderBy('expiry_date')->get()->map(function ($d) use ($today) {
            $left = $d->expiry_date ? (int) $today->diffInDays(Carbon::parse($d->expiry_date), false) : 9999;
            return ['type' => ucfirst((string) $d->doc_type), 'no' => $d->doc_no,
                'eq' => $d->equipment->code ?? ('#' . $d->equipment_id), 'by' => $d->issued_by ?? '',
                'issue' => $d->issue_date ? substr((string) $d->issue_date, 0, 10) : '',
                'exp' => $d->expiry_date ? substr((string) $d->expiry_date, 0, 10) : '',
                'left' => $left >= 9999 ? '—' : $left . 'd', 'cost' => round((float) $d->cost),
                'st' => $left < 0 ? 'red' : ($left <= 30 ? 'amber' : 'green'),
                'stL' => $left < 0 ? 'Expired' : ($left <= 30 ? 'Expiring' : 'Valid'),
                'open' => 'Open \u2192', 'openUrl' => '/app/equipment-documents/' . $d->id];
        })->all();
        return ['k' => 'table', 'id' => 'dc_eqdoc', 'ti' => 'Equipment Documents — insurance & roadworthy by expiry', 'cls' => 'wide', 'cfg' => [
            'id' => 'dc_eqdoc', 'page' => 12, 'search' => true,
            'cols' => [
                ['k' => 'type', 'l' => 'Type'], ['k' => 'no', 'l' => 'Doc no'], ['k' => 'eq', 'l' => 'Machine', 'wide' => 1],
                ['k' => 'by', 'l' => 'Issued by'], ['k' => 'issue', 'l' => 'Issued'], ['k' => 'exp', 'l' => 'Expiry'],
                ['k' => 'left', 'l' => 'Left'], ['k' => 'cost', 'l' => 'Cost', 'y' => 'num'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'], ['k' => 'open', 'l' => 'Open', 'y' => 'link'],
            ], 'rows' => $rows, 'totals' => ['cost'],
        ]];
    }

    private function secPtwTab(): array
    {
        $rows = WorkPermit::with(['quarry', 'project'])->orderByDesc('permit_date')->take(30)->get()
            ->map(fn($p) => ['no' => $p->permit_no, 'type' => ucfirst((string) str_replace('-', ' ', (string) $p->work_type)),
                'site' => $p->quarry->name ?? ($p->project->project_no ?? ''),
                'from' => substr((string) $p->valid_from, 0, 10), 'to' => substr((string) $p->valid_to, 0, 10),
                'req' => $p->requested_by ?? '', 'appr' => $p->approved_by ?? '',
                'st' => $p->status === 'approved' ? 'green' : ($p->status === 'pending' ? 'amber' : ($p->status === 'closed' ? 'blue' : 'red')),
                'stL' => ucfirst((string) $p->status),
                'open' => 'Open \u2192', 'openUrl' => '/app/work-permits/' . $p->id])->all();
        return ['k' => 'table', 'id' => 'dc_ptw', 'ti' => 'Work Permits — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'dc_ptw', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'no', 'l' => 'Permit'], ['k' => 'type', 'l' => 'Work type', 'wide' => 1], ['k' => 'site', 'l' => 'Site'],
                ['k' => 'from', 'l' => 'Valid from'], ['k' => 'to', 'l' => 'Valid to'],
                ['k' => 'req', 'l' => 'Requested by'], ['k' => 'appr', 'l' => 'Approved by'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'], ['k' => 'open', 'l' => 'Open', 'y' => 'link'],
            ], 'rows' => $rows,
        ]];
    }

    private function secGap($today): array
    {
        $docs = EquipmentDocument::all()->groupBy('equipment_id');
        $rows = Equipment::orderBy('code')->get()->map(function ($e) use ($docs, $today) {
            $g = $docs[$e->id] ?? collect();
            $has = fn($kw) => $g->contains(fn($d) => stripos((string) $d->doc_type, $kw) !== false
                && (!$d->expiry_date || Carbon::parse($d->expiry_date)->gt($today)));
            $ins = $has('insur');
            $road = $has('road');
            $ok = $ins && $road;
            return ['eq' => $e->code,
                'ins' => $ins ? 'Valid' : 'Missing/expired', 'road' => $road ? 'Valid' : 'Missing/expired',
                'act' => $ok ? 'None' : 'Renew ' . implode(' + ', array_filter([$ins ? null : 'insurance', $road ? null : 'roadworthy'])),
                'st' => $ok ? 'green' : 'red', 'stL' => $ok ? 'Covered' : 'Gap'];
        })->filter(fn($r) => $r['st'] === 'red')->values()->all();
        return ['k' => 'table', 'id' => 'dc_gap', 'ti' => 'Compliance Gaps — machines missing valid insurance or roadworthy', 'cls' => 'wide', 'cfg' => [
            'id' => 'dc_gap', 'page' => 12, 'search' => true,
            'cols' => [
                ['k' => 'eq', 'l' => 'Machine', 'wide' => 1], ['k' => 'ins', 'l' => 'Insurance'],
                ['k' => 'road', 'l' => 'Roadworthy'], ['k' => 'act', 'l' => 'Action'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows,
        ]];
    }
}
