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

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\Company;
use App\Models\Department;
use App\Models\DocumentSequence;
use App\Models\FiscalYear;
use App\Models\Permission;
use App\Models\Quarry;
use App\Models\Role;
use App\Models\Setting;
use App\Models\TaxRate;
use App\Models\User;
use App\Models\Warehouse;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

/** Live data for the Command Centre — Setup & Configuration / Admin module (serves #/setup and #/admin). */
class AdminController extends Controller
{
    use Concerns\DashboardHelpers;

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

        $active = User::where('is_active', true)->count();
        $inact = User::where('is_active', false)->count();
        $todayS = $today->format('Y-m-d');
        $logged = User::whereDate('last_login_at', $todayS)->count();
        $never = User::whereNull('last_login_at')->count();

        $fy = FiscalYear::where('is_current', true)->first() ?? FiscalYear::first();
        $fyProg = 0;
        $fyLabel = '—';
        if ($fy) {
            $fyLabel = $fy->name . ($fy->is_closed ? ' (closed)' : '');
            $s = Carbon::parse($fy->start_date);
            $e = Carbon::parse($fy->end_date);
            $fyProg = round(min(100, max(0, $s->diffInDays(min($today, $e)) / max(1, $s->diffInDays($e)) * 100)), 1);
        }

        $exps = [];
        foreach (Quarry::all() as $q) {
            if ($q->licence_expiry) $exps[] = $today->diffInDays(Carbon::parse($q->licence_expiry), false);
            if ($q->epa_expiry) $exps[] = $today->diffInDays(Carbon::parse($q->epa_expiry), false);
        }
        $minExp = $exps ? (int) min($exps) : 0;

        $co = Company::first();

        $cards = [
            $this->card('Active Users', $active, '#', $active, [], ['st' => 'blue']),
            $this->card('Logged In Today', $logged, '#', $logged, [], ['st' => 'green']),
            $this->card('Never Logged In', $never, '#', $never, [], ['good' => 'down', 'st' => $never > 0 ? 'amber' : 'green']),
            $this->card('Inactive Users', $inact, '#', $inact, [], ['good' => 'down', 'st' => $inact > 0 ? 'amber' : 'green']),
            $this->card('Roles Defined', Role::count(), '#', Role::count(), [], ['st' => 'blue']),
            $this->card('Permissions', Permission::count(), '#', Permission::count(), [], ['st' => 'blue']),
            $this->card('Earliest Permit Expiry', $minExp, 'num', $minExp, [], ['good' => 'up', 'tgt' => 180, 'info' => 'days']),
            $this->card('FY Progress', $fyProg, '%', $fyProg, [], ['st' => 'blue', 'info' => $fyLabel]),
            $this->card('Doc Sequences', DocumentSequence::count(), '#', DocumentSequence::count(), [], ['st' => 'blue']),
            $this->card('Tax Rates', TaxRate::where('is_active', true)->count(), '#', TaxRate::where('is_active', true)->count(), [], ['st' => 'blue']),
            $this->card('Warehouses', Warehouse::where('is_active', true)->count(), '#', Warehouse::where('is_active', true)->count(), [], ['st' => 'blue']),
            $this->card('Departments', Department::where('is_active', true)->count(), '#', Department::where('is_active', true)->count(), [], ['st' => 'blue']),
        ];

        return response()->json([
            'sub' => ($co->name ?? 'Group') . ' · users & roles · sites · fiscal · Group · ' . $r->get('period', 'This Month'),
            'asAt' => Carbon::today()->format('d M Y'),
            'cards' => $cards,
            'sections' => [
                $this->secPerm(),
                $this->secRoles(),
                $this->secLic($today),
                $this->secAudit(),
                $this->secUsers(),
                $this->secRolesTab(),
                $this->secSites($today),
                $this->secSeq(),
                $this->secTax(),
                $this->secSet(),
            ],
        ]);
    }

    private function secPerm(): array
    {
        $rows = Permission::select('module', DB::raw('count(*) n'))->groupBy('module')->orderByDesc('n')->get();
        return ['k' => 'chart', 'id' => 'ad_perm', 'ti' => 'Permissions by Module — count (live)', 'spec' => [
            'type' => 'hbar', 'fmt' => 'num',
            'labels' => $rows->map(fn($r) => ucfirst((string) $r->module))->all(),
            'series' => [['name' => 'Permissions', 'data' => $rows->map(fn($r) => (int) $r->n)->all(), 'color' => '#8b5cf6']],
        ]];
    }

    private function secRoles(): array
    {
        $rows = DB::table('role_user')->select('role_id', DB::raw('count(*) n'))->groupBy('role_id')->get();
        $names = Role::whereIn('id', $rows->pluck('role_id'))->pluck('name', 'id');
        $pal = $this->palette();
        $items = $rows->values()->map(fn($r, $i) => ['label' => $names[$r->role_id] ?? ('#' . $r->role_id),
            'value' => (int) $r->n, 'color' => $pal[$i % count($pal)]])->all();
        return ['k' => 'chart', 'id' => 'ad_roles', 'ti' => 'User Assignments by Role — count (live)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'num', 'center' => 'Users', 'items' => $items,
        ]];
    }

    private function secLic($today): array
    {
        $labels = [];
        $data = [];
        foreach (Quarry::orderBy('code')->get() as $q) {
            if ($q->licence_expiry) {
                $labels[] = $q->code . ' Mining licence';
                $data[] = (int) $today->diffInDays(Carbon::parse($q->licence_expiry), false);
            }
            if ($q->epa_expiry) {
                $labels[] = $q->code . ' EPA permit';
                $data[] = (int) $today->diffInDays(Carbon::parse($q->epa_expiry), false);
            }
        }
        return ['k' => 'chart', 'id' => 'ad_lic', 'ti' => 'Licence Runway — days to expiry (live)', 'spec' => [
            'type' => 'hbar', 'fmt' => 'num',
            'labels' => $labels,
            'series' => [['name' => 'Days', 'data' => $data, 'color' => '#22c55e']],
        ]];
    }

    private function secAudit(): array
    {
        $t = Carbon::today();
        $labels = [];
        $data = [];
        for ($i = 29; $i >= 0; $i--) {
            $d = $t->copy()->subDays($i)->format('Y-m-d');
            $labels[] = substr($d, 5);
            $data[] = AuditLog::whereDate('created_at', $d)->count();
        }
        return ['k' => 'chart', 'id' => 'ad_audit', 'ti' => 'System Load — audit events per day (30 days)', 'cls' => 'wide', 'spec' => [
            'type' => 'line', 'fmt' => 'num',
            'labels' => $labels,
            'series' => [['name' => 'Events', 'data' => $data, 'color' => '#3b82f6']],
        ]];
    }

    private function secUsers(): array
    {
        $rows = User::with('roles')->orderBy('username')->get()->map(fn($u) => [
            'user' => $u->username, 'name' => $u->name, 'email' => $u->email ?? '',
            'roles' => $u->roles->pluck('name')->implode(', ') ?: '—',
            'last' => $u->last_login_at ? substr((string) $u->last_login_at, 0, 16) : 'Never',
            'since' => $u->created_at ? substr((string) $u->created_at, 0, 10) : '',
            'st' => $u->is_active ? 'green' : 'red', 'stL' => $u->is_active ? 'Active' : 'Inactive',
            'open' => 'Edit \u2192', 'openUrl' => '/users/' . $u->id . '/edit'])->all();
        return ['k' => 'table', 'id' => 'ad_users', 'ti' => 'Users — access, roles & last login', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_users', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'user', 'l' => 'Username'], ['k' => 'name', 'l' => 'Name', 'wide' => 1], ['k' => 'email', 'l' => 'Email'],
                ['k' => 'roles', 'l' => 'Roles'], ['k' => 'last', 'l' => 'Last login'], ['k' => 'since', 'l' => 'Created'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'], ['k' => 'open', 'l' => 'Open', 'y' => 'link'],
            ], 'rows' => $rows,
        ]];
    }

    private function secRolesTab(): array
    {
        $rows = Role::withCount(['users', 'permissions'])->orderBy('name')->get()->map(fn($r) => [
            'name' => $r->name, 'slug' => $r->slug, 'desc' => mb_strimwidth((string) $r->description, 0, 60, '…'),
            'users' => $r->users_count, 'perms' => $r->permissions_count,
            'st' => $r->is_system ? 'blue' : 'green', 'stL' => $r->is_system ? 'System' : 'Custom'])->all();
        return ['k' => 'table', 'id' => 'ad_roles', 'ti' => 'Roles — membership & permission counts', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_roles', 'page' => 13, 'search' => true,
            'cols' => [
                ['k' => 'name', 'l' => 'Role'], ['k' => 'slug', 'l' => 'Slug'], ['k' => 'desc', 'l' => 'Description', 'wide' => 1],
                ['k' => 'users', 'l' => 'Users', 'y' => 'num'], ['k' => 'perms', 'l' => 'Permissions', 'y' => 'num'],
                ['k' => 'st', 'l' => 'Type', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => ['users'],
        ]];
    }

    private function secSites($today): array
    {
        $rows = [];
        foreach (Quarry::orderBy('code')->get() as $q) {
            $lexp = $q->licence_expiry ? (int) $today->diffInDays(Carbon::parse($q->licence_expiry), false) : 99999;
            $eexp = $q->epa_expiry ? (int) $today->diffInDays(Carbon::parse($q->epa_expiry), false) : 99999;
            $min = min($lexp, $eexp);
            $rows[] = ['code' => $q->code, 'name' => $q->name, 'kind' => 'Quarry', 'loc' => $q->location ?? '',
                'lic' => ($q->licence_no ?? '—') . ' → ' . ($q->licence_expiry ? substr((string) $q->licence_expiry, 0, 10) : '—'),
                'epa' => ($q->epa_permit_no ?? '—') . ' → ' . ($q->epa_expiry ? substr((string) $q->epa_expiry, 0, 10) : '—'),
                'run' => $min >= 99999 ? '—' : $min . 'd',
                'st' => !$q->is_active ? 'red' : ($min < 180 ? 'amber' : 'green'),
                'stL' => !$q->is_active ? 'Inactive' : ($min < 180 ? 'Renew soon' : 'Licensed')];
        }
        foreach (Warehouse::orderBy('code')->get() as $w) {
            $rows[] = ['code' => $w->code, 'name' => $w->name, 'kind' => 'Store', 'loc' => $w->location ?? '',
                'lic' => '—', 'epa' => '—', 'run' => '—',
                'st' => $w->is_active ? 'green' : 'red', 'stL' => $w->is_active ? 'Active' : 'Inactive'];
        }
        return ['k' => 'table', 'id' => 'ad_sites', 'ti' => 'Sites & Stores — quarries, licences & warehouses', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_sites', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'code', 'l' => 'Code'], ['k' => 'name', 'l' => 'Name', 'wide' => 1], ['k' => 'kind', 'l' => 'Kind'],
                ['k' => 'loc', 'l' => 'Location'], ['k' => 'lic', 'l' => 'Mining licence'], ['k' => 'epa', 'l' => 'EPA permit'],
                ['k' => 'run', 'l' => 'Runway'], ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows,
        ]];
    }

    private function secSeq(): array
    {
        $rows = DocumentSequence::orderBy('label')->get()->map(fn($s) => [
            'label' => $s->label, 'key' => $s->key, 'prefix' => $s->prefix,
            'next' => $s->prefix . '-' . str_pad((string) $s->next_number, (int) $s->padding, '0', STR_PAD_LEFT),
            'pad' => $s->padding, 'fy' => $s->include_fy ? 'Yes' : 'No',
            'st' => 'green', 'stL' => 'Active'])->all();
        return ['k' => 'table', 'id' => 'ad_seq', 'ti' => 'Document Numbering — next numbers per sequence', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_seq', 'page' => 12, 'search' => true,
            'cols' => [
                ['k' => 'label', 'l' => 'Document', 'wide' => 1], ['k' => 'key', 'l' => 'Key'], ['k' => 'prefix', 'l' => 'Prefix'],
                ['k' => 'next', 'l' => 'Next number'], ['k' => 'pad', 'l' => 'Pad'], ['k' => 'fy', 'l' => 'FY?'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows,
        ]];
    }

    private function secTax(): array
    {
        $rows = TaxRate::orderBy('code')->get()->map(fn($t) => [
            'code' => $t->code, 'name' => $t->name, 'rate' => (float) $t->rate_percent . '%',
            'on' => $t->applies_on ?? '', 'from' => $t->effective_from ? substr((string) $t->effective_from, 0, 10) : '',
            'st' => $t->is_active ? 'green' : 'red', 'stL' => $t->is_active ? 'Active' : 'Inactive'])->all();
        return ['k' => 'table', 'id' => 'ad_tax', 'ti' => 'Statutory Tax Rates — GRA VAT, levies & WHT', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_tax', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'code', 'l' => 'Code'], ['k' => 'name', 'l' => 'Tax', 'wide' => 1], ['k' => 'rate', 'l' => 'Rate'],
                ['k' => 'on', 'l' => 'Applies on'], ['k' => 'from', 'l' => 'Effective'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows,
        ]];
    }

    private function secSet(): array
    {
        $rows = Setting::query()->orderBy('group')->orderBy('key')->get()->map(fn($s) => [
            'group' => $s->group, 'key' => $s->key, 'label' => $s->label ?? '',
            'val' => mb_strimwidth((string) $s->value, 0, 60, '…'), 'type' => $s->type ?? '',
            'st' => 'blue', 'stL' => 'Set'])->all();
        return ['k' => 'table', 'id' => 'ad_set', 'ti' => 'System Settings — company, payroll & sales defaults', 'cls' => 'wide', 'cfg' => [
            'id' => 'ad_set', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'group', 'l' => 'Group'], ['k' => 'key', 'l' => 'Key'], ['k' => 'label', 'l' => 'Label', 'wide' => 1],
                ['k' => 'val', 'l' => 'Value'], ['k' => 'type', 'l' => 'Type'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows,
        ]];
    }
}
