<?php

namespace Modules\Inventory\Services;

use App\Models\ActivityLogModel;
use App\Models\StockBalanceModel;
use App\Models\StockCountLineModel;
use App\Models\StockCountModel;

class StockCountService
{
    protected StockCountModel $counts;
    protected StockCountLineModel $lines;
    protected StockBalanceModel $balances;
    protected StockService $stock;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->counts   = new StockCountModel();
        $this->lines    = new StockCountLineModel();
        $this->balances = new StockBalanceModel();
        $this->stock    = new StockService();
        $this->activity = new ActivityLogModel();
    }

    public function create(array $input, ?int $actingUserId): array
    {
        $warehouseId = (int) ($input['warehouse_id'] ?? 0);
        if ($warehouseId <= 0) {
            return ['success' => false, 'errors' => ['warehouse_id' => 'Warehouse is required.']];
        }

        $data = [
            'number'       => $this->counts->nextNumber(),
            'count_date'   => $input['count_date'] ?? date('Y-m-d'),
            'warehouse_id' => $warehouseId,
            'status'       => 'draft',
            'notes'        => trim($input['notes'] ?? '') ?: null,
            'created_by'   => $actingUserId,
            'updated_by'   => $actingUserId,
        ];

        if (! $this->counts->insert($data, true)) {
            return ['success' => false, 'errors' => $this->counts->errors()];
        }

        $id = $this->counts->getInsertID();
        $this->replaceLines($id, $warehouseId, $input['lines'] ?? []);
        $this->activity->log('stock_counts', 'created', $actingUserId, "Created stock count #{$id} ({$data['number']})", (string) $id);

        return ['success' => true, 'id' => $id];
    }

    public function update(int $id, array $input, ?int $actingUserId): array
    {
        $count = $this->counts->find($id);
        if (! $count) {
            return ['success' => false, 'errors' => ['id' => 'Stock count not found.']];
        }

        if (! in_array($count['status'], ['draft', 'in_progress'], true)) {
            return ['success' => false, 'errors' => ['status' => 'Only draft or in-progress counts can be edited.']];
        }

        $warehouseId = (int) ($input['warehouse_id'] ?? $count['warehouse_id']);
        if ($warehouseId <= 0) {
            return ['success' => false, 'errors' => ['warehouse_id' => 'Warehouse is required.']];
        }

        $data = [
            'count_date'   => $input['count_date'] ?? $count['count_date'],
            'warehouse_id' => $warehouseId,
            'notes'        => trim($input['notes'] ?? '') ?: null,
            'updated_by'   => $actingUserId,
        ];

        if (! $this->counts->update($id, $data)) {
            return ['success' => false, 'errors' => $this->counts->errors()];
        }

        $this->replaceLines($id, $warehouseId, $input['lines'] ?? []);
        $this->activity->log('stock_counts', 'updated', $actingUserId, "Updated stock count #{$id}", (string) $id);

        return ['success' => true];
    }

    public function complete(int $id, ?int $actingUserId): array
    {
        $db = \Config\Database::connect();
        $db->transStart();

        $count = $this->counts->find($id);
        if (! $count) {
            $db->transRollback();

            return ['success' => false, 'error' => 'Stock count not found.'];
        }

        if (! in_array($count['status'], ['draft', 'in_progress'], true)) {
            $db->transRollback();

            return ['success' => false, 'error' => 'Only draft or in-progress counts can be completed.'];
        }

        $lines = $this->lines->getForCount($id);
        if ($lines === []) {
            $db->transRollback();

            return ['success' => false, 'error' => 'Add at least one line before completing.'];
        }

        $reason = 'Stock count ' . $count['number'];

        foreach ($lines as $line) {
            if ($line['counted_qty'] === null || $line['counted_qty'] === '') {
                continue;
            }

            $countedQty = (float) $line['counted_qty'];
            $systemQty  = (float) $line['system_qty'];
            $variance   = $countedQty - $systemQty;

            $this->lines->update($line['id'], ['variance' => $variance]);

            $result = $this->stock->adjust([
                'product_id'   => (int) $line['product_id'],
                'warehouse_id' => (int) $count['warehouse_id'],
                'quantity'     => $countedQty,
                'mode'         => 'set',
                'reason'       => $reason,
                'notes'        => trim($line['notes'] ?? '') ?: null,
            ], $actingUserId);

            if (! $result['success']) {
                $db->transRollback();

                return [
                    'success' => false,
                    'error'   => ($line['product_name'] ?? 'Product') . ': ' . ($result['errors']['quantity'] ?? 'Adjustment failed.'),
                ];
            }
        }

        $this->counts->update($id, [
            'status'       => 'completed',
            'completed_at' => date('Y-m-d H:i:s'),
            'completed_by' => $actingUserId,
            'updated_by'   => $actingUserId,
        ]);

        $db->transComplete();

        if (! $db->transStatus()) {
            return ['success' => false, 'error' => 'Failed to complete stock count.'];
        }

        $this->activity->log('stock_counts', 'completed', $actingUserId, "Completed stock count #{$id}", (string) $id);

        return ['success' => true];
    }

    public function cancel(int $id, ?int $actingUserId): array
    {
        $count = $this->counts->find($id);
        if (! $count) {
            return ['success' => false, 'error' => 'Stock count not found.'];
        }

        if (! in_array($count['status'], ['draft', 'in_progress'], true)) {
            return ['success' => false, 'error' => 'Only draft or in-progress counts can be cancelled.'];
        }

        $this->counts->update($id, [
            'status'     => 'cancelled',
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('stock_counts', 'cancelled', $actingUserId, "Cancelled stock count #{$id}", (string) $id);

        return ['success' => true];
    }

    public function delete(int $id, ?int $actingUserId): array
    {
        $count = $this->counts->find($id);
        if (! $count) {
            return ['success' => false, 'error' => 'Stock count not found.'];
        }

        if (! in_array($count['status'], ['draft', 'cancelled'], true)) {
            return ['success' => false, 'error' => 'Only draft or cancelled counts can be deleted.'];
        }

        $this->lines->where('stock_count_id', $id)->delete();
        $this->counts->delete($id);
        $this->activity->log('stock_counts', 'deleted', $actingUserId, "Deleted stock count #{$id}", (string) $id);

        return ['success' => true];
    }

    protected function replaceLines(int $countId, int $warehouseId, array $linesInput): void
    {
        $this->lines->where('stock_count_id', $countId)->delete();

        $parsed = $this->parseLinesInput($linesInput);
        $sort   = 0;

        foreach ($parsed as $line) {
            $productId = (int) ($line['product_id'] ?? 0);
            if ($productId <= 0) {
                continue;
            }

            $systemQty = $line['system_qty'] ?? null;
            if ($systemQty === null || $systemQty === '') {
                $balance   = $this->balances->findByProductAndWarehouse($productId, $warehouseId);
                $systemQty = $balance ? (float) $balance['quantity'] : 0.0;
            } else {
                $systemQty = (float) $systemQty;
            }

            $countedQty = ($line['counted_qty'] !== null && $line['counted_qty'] !== '')
                ? (float) $line['counted_qty']
                : null;

            $variance = ($countedQty !== null) ? $countedQty - $systemQty : null;

            $this->lines->insert([
                'stock_count_id' => $countId,
                'product_id'     => $productId,
                'system_qty'     => $systemQty,
                'counted_qty'    => $countedQty,
                'variance'       => $variance,
                'notes'          => trim($line['notes'] ?? '') ?: null,
                'sort_order'     => $sort++,
                'created_at'     => date('Y-m-d H:i:s'),
            ]);
        }
    }

    /**
     * @return list<array{product_id: int, system_qty: mixed, counted_qty: mixed, notes: string}>
     */
    protected function parseLinesInput(array $linesInput): array
    {
        if (isset($linesInput['product_id']) && is_array($linesInput['product_id'])) {
            $productIds  = $linesInput['product_id'];
            $systemQtys  = $linesInput['system_qty'] ?? [];
            $countedQtys = $linesInput['counted_qty'] ?? [];
            $notesList   = $linesInput['notes'] ?? [];
            $parsed      = [];

            foreach ($productIds as $i => $productId) {
                $parsed[] = [
                    'product_id'  => (int) $productId,
                    'system_qty'  => $systemQtys[$i] ?? null,
                    'counted_qty' => $countedQtys[$i] ?? null,
                    'notes'       => $notesList[$i] ?? '',
                ];
            }

            return $parsed;
        }

        $parsed = [];
        foreach ($linesInput as $line) {
            if (! is_array($line)) {
                continue;
            }
            $parsed[] = [
                'product_id'  => (int) ($line['product_id'] ?? 0),
                'system_qty'  => $line['system_qty'] ?? null,
                'counted_qty' => $line['counted_qty'] ?? null,
                'notes'       => $line['notes'] ?? '',
            ];
        }

        return $parsed;
    }
}
