<?php
declare(strict_types=1);

namespace App\Modules\Inventory\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;

final class StockAuditService
{
    /**
     * Create a new stock audit and populate its items.
     */
    public static function createAudit(int $warehouseId, string $auditType, ?int $categoryId = null, ?string $notes = null): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $auditNumber = InventoryNumberGenerator::nextAuditNumber();

        // Determine items to audit
        $itemWhere = 'iws.`warehouse_id` = ?';
        $itemParams = [$warehouseId];

        if ($auditType === 'partial' && $categoryId) {
            $itemWhere .= ' AND i.`category_id` = ?';
            $itemParams[] = $categoryId;
        }

        $stockItems = $db->select(
            "SELECT iws.`item_id`, iws.`quantity` as system_quantity
             FROM `item_warehouse_stock` iws
             JOIN `inventory_items` i ON i.`id` = iws.`item_id` AND i.`is_archived` = 0
             WHERE {$itemWhere}
             ORDER BY i.`name_ar` ASC",
            $itemParams
        );

        // For spot_check, only take a random subset (max 20 items)
        if ($auditType === 'spot_check' && count($stockItems) > 20) {
            shuffle($stockItems);
            $stockItems = array_slice($stockItems, 0, 20);
        }

        $db->beginTransaction();
        try {
            $auditId = $db->insert('stock_audits', [
                'audit_number'  => $auditNumber,
                'warehouse_id'  => $warehouseId,
                'audit_type'    => $auditType,
                'category_id'   => $categoryId,
                'status'        => 'in_progress',
                'audit_date'    => date('Y-m-d'),
                'started_at'    => date('Y-m-d H:i:s'),
                'total_items'   => count($stockItems),
                'items_counted' => 0,
                'variance_count' => 0,
                'notes'         => $notes,
                'created_by'    => $employee ? (int) $employee->id : null,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ]);

            foreach ($stockItems as $si) {
                $db->insert('stock_audit_items', [
                    'audit_id'        => $auditId,
                    'item_id'         => (int) $si['item_id'],
                    'system_quantity' => (string) $si['system_quantity'],
                    'status'          => 'pending',
                ]);
            }

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        Logger::info("Audit #{$auditId} ({$auditNumber}) created: {$auditType} for warehouse #{$warehouseId}, " . count($stockItems) . " items");

        return $auditId;
    }

    /**
     * Save physical counts for audit items.
     *
     * @param array $counts  [audit_item_id => physical_quantity, ...]
     */
    public static function saveCounts(int $auditId, array $counts): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();
        $ts = date('Y-m-d H:i:s');

        foreach ($counts as $auditItemId => $physicalQty) {
            $item = $db->selectOne(
                "SELECT * FROM `stock_audit_items` WHERE `id` = ? AND `audit_id` = ?",
                [(int) $auditItemId, $auditId]
            );
            if (!$item) continue;

            $systemQty = (string) $item['system_quantity'];
            $physicalQty = (string) $physicalQty;
            $variance = bcsub($physicalQty, $systemQty, 3);

            // Get cost for variance calculation
            $itemData = $db->selectOne("SELECT `cost_price` FROM `inventory_items` WHERE `id` = ?", [(int) $item['item_id']]);
            $varianceCost = $itemData ? bcmul($variance, (string) ($itemData['cost_price'] ?? '0'), 2) : null;

            $db->update('stock_audit_items', [
                'physical_quantity' => $physicalQty,
                'variance'          => $variance,
                'variance_cost'     => $varianceCost,
                'status'            => 'counted',
                'counted_by'        => $employee ? (int) $employee->id : null,
                'counted_at'        => $ts,
            ], '`id` = ?', [(int) $auditItemId]);
        }

        // Update audit summary
        $counted = $db->selectOne(
            "SELECT COUNT(*) as cnt FROM `stock_audit_items` WHERE `audit_id` = ? AND `status` = 'counted'",
            [$auditId]
        );
        $variances = $db->selectOne(
            "SELECT COUNT(*) as cnt FROM `stock_audit_items` WHERE `audit_id` = ? AND `status` = 'counted' AND `variance` != 0",
            [$auditId]
        );

        $db->update('stock_audits', [
            'items_counted'  => (int) ($counted['cnt'] ?? 0),
            'variance_count' => (int) ($variances['cnt'] ?? 0),
            'status'         => 'pending_approval',
            'completed_at'   => $ts,
            'updated_at'     => $ts,
        ], '`id` = ?', [$auditId]);
    }

    /**
     * Approve an audit and create adjustment movements for variances.
     */
    public static function approveAudit(int $auditId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $audit = $db->selectOne("SELECT * FROM `stock_audits` WHERE `id` = ?", [$auditId]);
        if (!$audit) {
            throw new \RuntimeException('الجرد غير موجود');
        }
        if ($audit['status'] !== 'pending_approval') {
            throw new \RuntimeException('الجرد ليس في حالة انتظار الاعتماد');
        }

        $warehouseId = (int) $audit['warehouse_id'];

        // Get items with variances
        $items = $db->select(
            "SELECT * FROM `stock_audit_items` WHERE `audit_id` = ? AND `status` = 'counted' AND `variance` != 0",
            [$auditId]
        );

        $db->beginTransaction();
        try {
            foreach ($items as $item) {
                $variance = (string) $item['variance'];
                $direction = bccomp($variance, '0', 3) > 0 ? 'in' : 'out';
                $absVariance = ltrim($variance, '-');

                StockService::moveStock([
                    'item_id'        => (int) $item['item_id'],
                    'warehouse_id'   => $warehouseId,
                    'movement_type'  => $direction === 'in' ? 'adjustment_in' : 'adjustment_out',
                    'direction'      => $direction,
                    'quantity'       => $absVariance,
                    'reference_type' => 'stock_audits',
                    'reference_id'   => $auditId,
                    'notes'          => 'تسوية جرد — ' . $audit['audit_number'],
                ]);

                $db->update('stock_audit_items', [
                    'status' => 'approved',
                ], '`id` = ?', [(int) $item['id']]);
            }

            $db->update('stock_audits', [
                'status'      => 'approved',
                'approved_by' => $employee ? (int) $employee->id : null,
                'approved_at' => date('Y-m-d H:i:s'),
                'updated_at'  => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$auditId]);

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        EventBus::dispatch('inventory.audit_completed', [
            'audit_id'       => $auditId,
            'warehouse_id'   => $warehouseId,
            'variance_count' => count($items),
        ]);

        Logger::info("Audit #{$auditId} approved — " . count($items) . " adjustment(s) created");
    }
}
