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

use App\Http\Controllers\Controller;
use App\Models\HseIncident;
use App\Models\HseInspection;
use App\Models\PpeIssue;
use App\Models\Warehouse;
use App\Models\WorkPermit;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

/** Live data for the Command Centre — Health & Safety module. */
class HseController extends Controller
{
    use Concerns\DashboardHelpers;

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

        $incQ = fn() => HseIncident::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry));
        $open = (clone $incQ())->where('status', 'open')->count();
        $inc = (clone $incQ())->whereBetween('incident_date', [$cf, $ct])->count();
        $incP = (clone $incQ())->whereBetween('incident_date', [$pf, $pt])->count();
        $lti = (clone $incQ())->whereBetween('incident_date', [$cf, $ct])->where('lost_time_days', '>', 0)->count();
        $ltiP = (clone $incQ())->whereBetween('incident_date', [$pf, $pt])->where('lost_time_days', '>', 0)->count();
        $ltd = $this->sumBetween($incQ(), 'incident_date', 'lost_time_days', $cf, $ct);
        $ltdP = $this->sumBetween($incQ(), 'incident_date', 'lost_time_days', $pf, $pt);
        $fatal = (clone $incQ())->whereBetween('incident_date', [$cf, $ct])->where('severity', 'fatal')->count();
        $fatalP = (clone $incQ())->whereBetween('incident_date', [$pf, $pt])->where('severity', 'fatal')->count();
        $lastInc = (clone $incQ())->max('incident_date');
        $daysSince = $lastInc ? Carbon::parse($lastInc)->diffInDays(Carbon::today()) : 0;

        $inspQ = fn() => HseInspection::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry));
        $inspOpen = (clone $inspQ())->where('status', 'open')->count();
        $insp = (clone $inspQ())->whereBetween('inspection_date', [$cf, $ct])->count();
        $inspP = (clone $inspQ())->whereBetween('inspection_date', [$pf, $pt])->count();
        $closed = (clone $incQ())->whereBetween('incident_date', [$cf, $ct])->where('status', 'closed')->count();
        $closure = $inc > 0 ? round($closed / $inc * 100, 1) : 100;

        $today = Carbon::today()->format('Y-m-d');
        $permQ = fn() => WorkPermit::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry));
        $active = (clone $permQ())->where('status', 'approved')->whereDate('valid_from', '<=', $today)->whereDate('valid_to', '>=', $today)->count();
        $pend = (clone $permQ())->where('status', 'pending')->count();

        $whIds = Warehouse::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))->pluck('id')->all();
        $ppe = PpeIssue::when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))->whereBetween('issue_date', [$cf, $ct])->count();
        $ppeP = PpeIssue::when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))->whereBetween('issue_date', [$pf, $pt])->count();

        $cards = [
            $this->card('Open Incidents', $open, '#', $open, [], ['good' => 'down', 'st' => $open > 0 ? 'red' : 'green']),
            $this->card('Incidents (' . $r->get('period', 'This Month') . ')', $inc, '#', $incP, [], ['good' => 'down']),
            $this->card('Lost Time Injuries', $lti, '#', $ltiP, [], ['good' => 'down', 'st' => $lti > 0 ? 'red' : 'green']),
            $this->card('Lost Time Days', round($ltd, 1), 'num', round($ltdP, 1), [], ['good' => 'down']),
            $this->card('Fatalities', $fatal, '#', $fatalP, [], ['good' => 'down', 'st' => $fatal > 0 ? 'red' : 'green']),
            $this->card('Days Since Last Incident', $daysSince, 'num', $daysSince, [], ['good' => 'up']),
            $this->card('Open Inspections', $inspOpen, '#', $inspOpen, [], ['st' => $inspOpen > 0 ? 'amber' : 'green']),
            $this->card('Inspections', $insp, '#', $inspP),
            $this->card('Incident Closure Rate', $closure, '%', $closure, [], ['tgt' => 90]),
            $this->card('Active Work Permits', $active, '#', $active, [], ['st' => 'blue']),
            $this->card('Permits Pending Approval', $pend, '#', $pend, [], ['st' => $pend > 0 ? 'amber' : 'green']),
            $this->card('PPE Issues', $ppe, '#', $ppeP),
        ];

        return response()->json([
            'sub' => 'Incidents · injuries · inspections · permits · PPE · ' . $r->get('period', 'This Month'),
            'asAt' => Carbon::today()->format('d M Y'),
            'cards' => $cards,
            'sections' => [
                $this->secTrend($qry),
                $this->secSev($qry),
                $this->secType($qry),
                $this->secInspClose($qry),
                $this->secIncidents($qry),
                $this->secInspections($qry),
                $this->secPermits($qry),
                $this->secPpe($qry, $whIds),
            ],
        ]);
    }

    private function secTrend($qry): array
    {
        $t = Carbon::today();
        $labels = [];
        $data = [];
        for ($i = 11; $i >= 0; $i--) {
            $d = $t->copy()->subMonths($i);
            $labels[] = $d->format('M y');
            $data[] = HseIncident::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
                ->whereBetween('incident_date', [$d->copy()->startOfMonth()->format('Y-m-d'), $d->copy()->endOfMonth()->format('Y-m-d')])->count();
        }
        return ['k' => 'chart', 'id' => 'h_trend', 'ti' => 'Incidents per Month — count (12 months)', 'cls' => 'wide', 'spec' => [
            'type' => 'bar', 'fmt' => 'num',
            'labels' => $labels,
            'series' => [['name' => 'Incidents', 'data' => $data, 'color' => '#ef4444']],
        ]];
    }

    private function secSev($qry): array
    {
        $rows = HseIncident::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->select('severity', DB::raw('count(*) n'))->groupBy('severity')->get();
        $colors = ['low' => '#22c55e', 'medium' => '#f8c200', 'high' => '#f97316', 'fatal' => '#ef4444'];
        $pal = $this->palette();
        $items = $rows->values()->map(fn($r, $i) => ['label' => ucfirst((string) $r->severity), 'value' => (int) $r->n,
            'color' => $colors[strtolower((string) $r->severity)] ?? $pal[$i % count($pal)]])->all();
        return ['k' => 'chart', 'id' => 'h_sev', 'ti' => 'Incidents by Severity — count (all time)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'num', 'center' => 'Severity', 'items' => $items,
        ]];
    }

    private function secType($qry): array
    {
        $rows = HseIncident::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->select('incident_type', DB::raw('count(*) n'))->groupBy('incident_type')->orderByDesc('n')->get();
        return ['k' => 'chart', 'id' => 'h_type', 'ti' => 'Incidents by Type — count (all time)', 'spec' => [
            'type' => 'bar', 'fmt' => 'num',
            'labels' => $rows->map(fn($r) => ucwords(str_replace('-', ' ', (string) $r->incident_type)))->all(),
            'series' => [['name' => 'Count', 'data' => $rows->map(fn($r) => (int) $r->n)->all(), 'color' => '#f97316']],
        ]];
    }

    private function secInspClose($qry): array
    {
        $rows = HseInspection::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->select('status', DB::raw('count(*) n'))->groupBy('status')->pluck('n', 'status');
        $items = [
            ['label' => 'Closed', 'value' => (int) ($rows['closed'] ?? 0), 'color' => '#22c55e'],
            ['label' => 'Open', 'value' => (int) ($rows['open'] ?? 0), 'color' => '#f8c200'],
        ];
        return ['k' => 'chart', 'id' => 'h_insp', 'ti' => 'Inspections Open vs Closed — count (all time)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'num', 'center' => 'Insp.', 'items' => $items,
        ]];
    }

    private function secIncidents($qry): array
    {
        $rows = HseIncident::with('quarry')
            ->when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->orderByDesc('incident_date')->take(30)->get()
            ->map(fn($i) => ['no' => $i->incident_no, 'date' => substr((string) $i->incident_date, 0, 10),
                'site' => $i->quarry->name ?? '', 'type' => ucwords(str_replace('-', ' ', (string) $i->incident_type)),
                'sev' => ucfirst((string) $i->severity), 'loc' => $i->location ?? '',
                'desc' => mb_strimwidth((string) $i->description, 0, 70, '…'), 'ltd' => round((float) $i->lost_time_days, 1),
                'by' => $i->reported_by ?? '',
                'st' => $i->status === 'closed' ? 'green' : 'red', 'stL' => ucfirst((string) $i->status)])->all();
        return ['k' => 'table', 'id' => 'h_inc', 'ti' => 'Safety Incidents — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'h_inc', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'no', 'l' => 'Incident'], ['k' => 'date', 'l' => 'Date'], ['k' => 'site', 'l' => 'Site'],
                ['k' => 'type', 'l' => 'Type'], ['k' => 'sev', 'l' => 'Severity'], ['k' => 'loc', 'l' => 'Location'],
                ['k' => 'desc', 'l' => 'Description', 'wide' => 1], ['k' => 'ltd', 'l' => 'LTD', 'y' => 'num'],
                ['k' => 'by', 'l' => 'Reported by'], ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }

    private function secInspections($qry): array
    {
        $rows = HseInspection::with('quarry')
            ->when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->orderByDesc('inspection_date')->take(30)->get()
            ->map(fn($i) => ['no' => $i->inspection_no, 'date' => substr((string) $i->inspection_date, 0, 10),
                'site' => $i->quarry->name ?? '', 'type' => ucfirst((string) $i->inspection_type),
                'by' => $i->inspector ?? '', 'find' => mb_strimwidth((string) $i->findings, 0, 60, '…'),
                'act' => mb_strimwidth((string) $i->corrective_actions, 0, 60, '…'),
                'st' => $i->status === 'closed' ? 'green' : 'amber', 'stL' => ucfirst((string) $i->status)])->all();
        return ['k' => 'table', 'id' => 'h_insp', 'ti' => 'HSE Inspections — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'h_insp', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'no', 'l' => 'Inspection'], ['k' => 'date', 'l' => 'Date'], ['k' => 'site', 'l' => 'Site'],
                ['k' => 'type', 'l' => 'Type'], ['k' => 'by', 'l' => 'Inspector'],
                ['k' => 'find', 'l' => 'Findings', 'wide' => 1], ['k' => 'act', 'l' => 'Corrective actions'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }

    private function secPermits($qry): array
    {
        $today = Carbon::today()->format('Y-m-d');
        $rows = WorkPermit::with('quarry')
            ->when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))
            ->orderByDesc('permit_date')->take(30)->get()
            ->map(function ($w) use ($today) {
                $live = $w->status === 'approved' && $w->valid_from <= $today && $w->valid_to >= $today;
                return ['no' => $w->permit_no, 'site' => $w->quarry->name ?? '',
                    'type' => ucwords(str_replace('-', ' ', (string) $w->work_type)),
                    'desc' => mb_strimwidth((string) $w->description, 0, 60, '…'),
                    'from' => substr((string) $w->valid_from, 0, 10), 'to' => substr((string) $w->valid_to, 0, 10),
                    'by' => $w->requested_by ?? '',
                    'st' => $live ? 'green' : ($w->status === 'pending' ? 'amber' : 'grey'),
                    'stL' => $live ? 'Active' : ucfirst((string) $w->status)];
            })->all();
        return ['k' => 'table', 'id' => 'h_perm', 'ti' => 'Work Permits — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'h_perm', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'no', 'l' => 'Permit'], ['k' => 'site', 'l' => 'Site'], ['k' => 'type', 'l' => 'Work'],
                ['k' => 'desc', 'l' => 'Description', 'wide' => 1], ['k' => 'from', 'l' => 'Valid from'],
                ['k' => 'to', 'l' => 'Valid to'], ['k' => 'by', 'l' => 'Requested by'], ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }

    private function secPpe($qry, $whIds): array
    {
        $rows = PpeIssue::with(['employee', 'warehouse'])
            ->when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))
            ->orderByDesc('issue_date')->orderByDesc('id')->take(30)->get()
            ->map(fn($p) => ['date' => substr((string) $p->issue_date, 0, 10),
                'emp' => trim(($p->employee->first_name ?? '') . ' ' . ($p->employee->last_name ?? '')),
                'item' => $p->item_name, 'qty' => round((float) $p->qty, 1), 'wh' => $p->warehouse->code ?? '',
                'by' => $p->issued_by ?? ''])->all();
        return ['k' => 'table', 'id' => 'h_ppe', 'ti' => 'PPE Issues — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'h_ppe', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'date', 'l' => 'Date'], ['k' => 'emp', 'l' => 'Employee', 'wide' => 1], ['k' => 'item', 'l' => 'Item'],
                ['k' => 'qty', 'l' => 'Qty', 'y' => 'num'], ['k' => 'wh', 'l' => 'Store'], ['k' => 'by', 'l' => 'Issued by'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }
}
