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

use App\Http\Controllers\Controller;
use App\Models\PpeIssue;
use App\Models\StockAdjustment;
use App\Models\StockItem;
use App\Models\StockLedger;
use App\Models\Warehouse;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

/** Live data for the Command Centre — Inventory & Warehouse module. */
class InventoryController extends Controller
{
    use Concerns\DashboardHelpers;

    private ?array $flowCache = null;

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

        $ledQ = fn() => StockLedger::when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds));
        $inVal = (float) (clone $ledQ())->whereBetween('txn_date', [$cf, $ct])->selectRaw('sum(qty_in*rate) v')->value('v');
        $inValP = (float) (clone $ledQ())->whereBetween('txn_date', [$pf, $pt])->selectRaw('sum(qty_in*rate) v')->value('v');
        $outVal = (float) (clone $ledQ())->whereBetween('txn_date', [$cf, $ct])->selectRaw('sum(qty_out*rate) v')->value('v');
        $outValP = (float) (clone $ledQ())->whereBetween('txn_date', [$pf, $pt])->selectRaw('sum(qty_out*rate) v')->value('v');
        $receipts = (clone $ledQ())->whereBetween('txn_date', [$cf, $ct])->where('qty_in', '>', 0)->count();
        $receiptsP = (clone $ledQ())->whereBetween('txn_date', [$pf, $pt])->where('qty_in', '>', 0)->count();
        $issues = (clone $ledQ())->whereBetween('txn_date', [$cf, $ct])->where('qty_out', '>', 0)->count();
        $issuesP = (clone $ledQ())->whereBetween('txn_date', [$pf, $pt])->where('qty_out', '>', 0)->count();
        $adjs = (clone $ledQ())->whereBetween('txn_date', [$cf, $ct])->whereIn('txn_type', ['adjust-in', 'adjust-out'])->count();

        $items = StockItem::with('unit')->where('is_active', true)
            ->when($qry !== 'all', fn($q) => $q->whereIn('default_warehouse_id', $whIds))->get();
        $value = (float) $items->sum(fn($i) => $i->current_qty * $i->avg_cost_rate);
        $low = $items->filter(fn($i) => $i->current_qty > 0 && $i->current_qty < $i->reorder_level)->count();
        $out = $items->filter(fn($i) => $i->current_qty <= 0)->count();
        $over = $items->filter(fn($i) => $i->max_level > 0 && $i->current_qty > $i->max_level)->count();
        $whs = Warehouse::where('is_active', true)->when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))->count();

        $cards = [
            $this->card('Stock Value', round($value), 'GHS', round($value), [], ['st' => 'blue', 'info' => 'at average cost']),
            $this->card('Receipts Value (' . $r->get('period', 'This Month') . ')', round($inVal), 'GHS', round($inValP)),
            $this->card('Issues Value (' . $r->get('period', 'This Month') . ')', round($outVal), 'GHS', round($outValP)),
            $this->card('Net Movement', round($inVal - $outVal), 'GHS', round($inValP - $outValP)),
            $this->card('Receipt Txns', $receipts, '#', $receiptsP),
            $this->card('Issue Txns', $issues, '#', $issuesP),
            $this->card('Below Reorder Level', $low, '#', $low, [], ['good' => 'down', 'st' => $low > 0 ? 'red' : 'green']),
            $this->card('Stockouts', $out, '#', $out, [], ['good' => 'down', 'st' => $out > 0 ? 'red' : 'green']),
            $this->card('Overstocked', $over, '#', $over, [], ['good' => 'down', 'st' => $over > 0 ? 'amber' : 'green']),
            $this->card('Active Items', $items->count(), '#', $items->count(), [], ['st' => 'blue']),
            $this->card('Warehouses', $whs, '#', $whs, [], ['st' => 'blue']),
            $this->card('Adjustments', $adjs, '#', $adjs, [], ['st' => 'blue']),
        ];

        return response()->json([
            'sub' => 'Stock value · receipts · issues · reorder alerts · ' . $r->get('period', 'This Month'),
            'asAt' => Carbon::today()->format('d M Y'),
            'cards' => $cards,
            'sections' => [
                $this->secFlows($qry, $value),
                $this->secLevel($qry, $value),
                $this->secByCat($qry),
                $this->secTopIssued($qry, $cf, $ct),
                $this->secItems($qry),
                $this->secLedger($qry),
                $this->secAdj($qry),
                $this->secPpe($qry),
            ],
        ]);
    }

    private function whIds($qry): array
    {
        return Warehouse::when($qry !== 'all', fn($q) => $q->where('quarry_id', $qry))->pluck('id')->all();
    }

    /** Daily [in-value, out-value] for 30 days, memoized (shared by flows + level charts). */
    private function dayVals($qry): array
    {
        if ($this->flowCache === null) {
            $whIds = $this->whIds($qry);
            $t = Carbon::today();
            $in = [];
            $out = [];
            for ($i = 29; $i >= 0; $i--) {
                $d = $t->copy()->subDays($i)->format('Y-m-d');
                $row = StockLedger::when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))
                    ->whereDate('txn_date', $d)->selectRaw('sum(qty_in*rate) iv, sum(qty_out*rate) ov')->first();
                $in[] = round((float) $row->iv, 2);
                $out[] = round((float) $row->ov, 2);
            }
            $this->flowCache = [$in, $out];
        }
        return $this->flowCache;
    }

    private function secFlows($qry, $value): array
    {
        [$in, $out] = $this->dayVals($qry);
        return ['k' => 'chart', 'id' => 'iv_flows', 'ti' => 'Receipts vs Issues — GHS per day (30 days)', 'cls' => 'wide', 'spec' => [
            'type' => 'line', 'fmt' => 'money', 'zoom' => true, 'height' => 280,
            'labels' => $this->dailyLabels(30),
            'series' => [
                ['name' => 'Receipts', 'data' => $in, 'color' => '#22c55e', 'area' => true],
                ['name' => 'Issues', 'data' => $out, 'color' => '#ef4444'],
            ],
        ]];
    }

    private function secLevel($qry, $value): array
    {
        [$in, $out] = $this->dayVals($qry);
        $bal = $value - (array_sum($in) - array_sum($out));
        $data = [];
        for ($i = 0; $i < 30; $i++) {
            $bal += $in[$i] - $out[$i];
            $data[] = round(max($bal, 0));
        }
        return ['k' => 'chart', 'id' => 'iv_level', 'ti' => 'Stock Value on Hand — GHS per day (30 days)', 'cls' => 'wide', 'spec' => [
            'type' => 'line', 'fmt' => 'money', 'zoom' => true, 'height' => 260,
            'labels' => $this->dailyLabels(30),
            'series' => [['name' => 'Value', 'data' => $data, 'color' => '#f8c200', 'area' => true]],
            'target' => ['value' => count($data) ? round(array_sum($data) / count($data)) : 0, 'label' => '30-day avg'],
        ]];
    }

    private function secByCat($qry): array
    {
        $whIds = $this->whIds($qry);
        $rows = StockItem::where('is_active', true)
            ->when($qry !== 'all', fn($q) => $q->whereIn('default_warehouse_id', $whIds))
            ->select('category', DB::raw('sum(current_qty*avg_cost_rate) t'))->groupBy('category')->orderByDesc('t')->get();
        $pal = $this->palette();
        $items = $rows->values()->map(fn($r, $i) => ['label' => ucwords(str_replace('-', ' ', (string) $r->category)), 'value' => round((float) $r->t), 'color' => $pal[$i % count($pal)]])->all();
        return ['k' => 'chart', 'id' => 'iv_bycat', 'ti' => 'Stock Value by Category — GHS (live)', 'spec' => [
            'type' => 'doughnut', 'fmt' => 'money', 'center' => 'Value', 'items' => $items,
        ]];
    }

    private function secTopIssued($qry, $cf, $ct): array
    {
        $whIds = $this->whIds($qry);
        $rows = StockLedger::when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))
            ->whereBetween('txn_date', [$cf, $ct])->where('qty_out', '>', 0)
            ->select('item_id', DB::raw('sum(qty_out*rate) t'))->groupBy('item_id')->orderByDesc('t')->take(8)->get();
        $names = StockItem::whereIn('id', $rows->pluck('item_id'))->pluck('code', 'id');
        return ['k' => 'chart', 'id' => 'iv_top', 'ti' => 'Top Issued Items — GHS (period)', 'spec' => [
            'type' => 'hbar', 'fmt' => 'money',
            'labels' => $rows->map(fn($r) => $names[$r->item_id] ?? ('#' . $r->item_id))->all(),
            'series' => [['name' => 'GHS', 'data' => $rows->map(fn($r) => round((float) $r->t))->all(), 'color' => '#ef4444']],
        ]];
    }

    private function secItems($qry): array
    {
        $whIds = $this->whIds($qry);
        $rows = StockItem::with(['unit', 'warehouse'])->where('is_active', true)
            ->when($qry !== 'all', fn($q) => $q->whereIn('default_warehouse_id', $whIds))->orderBy('code')->get()
            ->map(function ($i) {
                $st = $i->current_qty <= 0 ? 'red' : ($i->current_qty < $i->reorder_level ? 'red' : (($i->max_level > 0 && $i->current_qty > $i->max_level) ? 'amber' : 'green'));
                $stL = $i->current_qty <= 0 ? 'Stockout' : ($i->current_qty < $i->reorder_level ? 'Reorder' : (($i->max_level > 0 && $i->current_qty > $i->max_level) ? 'Overstock' : 'OK'));
                return ['code' => $i->code, 'name' => $i->name,
                    'cat' => ucwords(str_replace('-', ' ', (string) $i->category)), 'unit' => $i->unit->symbol ?? '',
                    'wh' => $i->warehouse->code ?? '', 're' => round((float) $i->reorder_level, 1),
                    'max' => round((float) $i->max_level, 1), 'onhand' => round((float) $i->current_qty, 1),
                    'cost' => round((float) $i->avg_cost_rate, 2), 'val' => round($i->current_qty * $i->avg_cost_rate),
                    'st' => $st, 'stL' => $stL];
            })->all();
        return ['k' => 'table', 'id' => 'iv_items', 'ti' => 'Stock Items — live balances & alerts', 'cls' => 'wide', 'cfg' => [
            'id' => 'iv_items', 'page' => 12, 'search' => true,
            'cols' => [
                ['k' => 'code', 'l' => 'Code'], ['k' => 'name', 'l' => 'Item', 'wide' => 1], ['k' => 'cat', 'l' => 'Category'],
                ['k' => 'unit', 'l' => 'UoM'], ['k' => 'wh', 'l' => 'Store'], ['k' => 're', 'l' => 'Reorder', 'y' => 'num'],
                ['k' => 'max', 'l' => 'Max', 'y' => 'num'], ['k' => 'onhand', 'l' => 'On hand', 'y' => 'num'],
                ['k' => 'cost', 'l' => 'Avg cost', 'y' => 'num'], ['k' => 'val', 'l' => 'Value', 'y' => 'num'],
                ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => ['val'],
        ]];
    }

    private function secLedger($qry): array
    {
        $whIds = $this->whIds($qry);
        $rows = StockLedger::with(['item', 'warehouse'])
            ->when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))
            ->orderByDesc('txn_date')->orderByDesc('id')->take(60)->get()
            ->map(fn($l) => ['date' => substr((string) $l->txn_date, 0, 10), 'item' => $l->item->code ?? '',
                'name' => $l->item->name ?? '', 'wh' => $l->warehouse->code ?? '',
                'type' => ucwords(str_replace('-', ' ', (string) $l->txn_type)),
                'st' => $l->qty_in > 0 ? 'green' : 'amber', 'stL' => ucwords(str_replace('-', ' ', (string) $l->txn_type)),
                'ref' => trim(($l->ref_type ?? '') . ' ' . ($l->ref_id ?? '')),
                'in' => round((float) $l->qty_in, 2), 'out' => round((float) $l->qty_out, 2),
                'rate' => round((float) $l->rate, 2), 'bal' => round((float) $l->balance_after, 2)])->all();
        return ['k' => 'table', 'id' => 'iv_ledger', 'ti' => 'Stock Ledger — latest 60', 'cls' => 'wide', 'cfg' => [
            'id' => 'iv_ledger', 'page' => 12, 'search' => true,
            'cols' => [
                ['k' => 'date', 'l' => 'Date'], ['k' => 'item', 'l' => 'Code'], ['k' => 'name', 'l' => 'Item', 'wide' => 1],
                ['k' => 'wh', 'l' => 'Store'], ['k' => 'st', 'l' => 'Type', 'y' => 'badge'], ['k' => 'ref', 'l' => 'Reference'],
                ['k' => 'in', 'l' => 'In', 'y' => 'num'], ['k' => 'out', 'l' => 'Out', 'y' => 'num'],
                ['k' => 'rate', 'l' => 'Rate', 'y' => 'num'], ['k' => 'bal', 'l' => 'Balance', 'y' => 'num'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }

    private function secAdj($qry): array
    {
        $whIds = $this->whIds($qry);
        $rows = StockAdjustment::with('warehouse')
            ->when($qry !== 'all', fn($q) => $q->whereIn('warehouse_id', $whIds))
            ->orderByDesc('adjustment_date')->take(30)->get()
            ->map(fn($a) => ['no' => $a->adjustment_no, 'date' => substr((string) $a->adjustment_date, 0, 10),
                'wh' => $a->warehouse->code ?? '', 'reason' => mb_strimwidth((string) $a->reason, 0, 70, '…'),
                'st' => $a->status === 'approved' ? 'green' : 'amber', 'stL' => ucfirst((string) $a->status)])->all();
        return ['k' => 'table', 'id' => 'iv_adj', 'ti' => 'Stock Adjustments — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'iv_adj', 'page' => 10, 'search' => true,
            'cols' => [
                ['k' => 'no', 'l' => 'Adj No'], ['k' => 'date', 'l' => 'Date'], ['k' => 'wh', 'l' => 'Store'],
                ['k' => 'reason', 'l' => 'Reason', 'wide' => 1], ['k' => 'st', 'l' => 'Status', 'y' => 'badge'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }

    private function secPpe($qry): array
    {
        $whIds = $this->whIds($qry);
        $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 ?? '', 'ret' => $p->returnable ? 'Yes' : 'No'])->all();
        return ['k' => 'table', 'id' => 'iv_ppe', 'ti' => 'PPE Issues — latest 30', 'cls' => 'wide', 'cfg' => [
            'id' => 'iv_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'], ['k' => 'ret', 'l' => 'Returnable'],
            ], 'rows' => $rows, 'totals' => [],
        ]];
    }
}
