<?php
declare(strict_types=1);

namespace App\Modules\Inventory\Services;

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

final class MovementEngineService
{
    private static array $specCache = [];

    public static function getMovementType(string $code): ?array
    {
        $db = App::getInstance()->db();
        return $db->selectOne("SELECT * FROM movement_types WHERE code = ? AND is_active = 1", [$code]);
    }

    public static function getSpecifications(int $movementTypeId): array
    {
        if (isset(self::$specCache[$movementTypeId])) {
            return self::$specCache[$movementTypeId];
        }

        $db = App::getInstance()->db();
        $rows = $db->select(
            "SELECT spec_key, spec_value, spec_type FROM movement_specifications WHERE movement_type_id = ?",
            [$movementTypeId]
        );

        $specs = [];
        foreach ($rows as $row) {
            $specs[$row['spec_key']] = self::castValue($row['spec_value'], $row['spec_type']);
        }

        self::$specCache[$movementTypeId] = $specs;
        return $specs;
    }

    public static function executeMovement(string $typeCode, array $data): array
    {
        $type = self::getMovementType($typeCode);
        if (!$type) {
            return ['success' => false, 'error' => 'نوع الحركة غير موجود: ' . $typeCode];
        }

        $specs = self::getSpecifications((int) $type['id']);
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        if ($type['requires_approval'] && empty($data['skip_approval'])) {
            return ['success' => false, 'needs_approval' => true, 'message' => 'هذه الحركة تحتاج اعتماد'];
        }

        $db->beginTransaction();
        try {
            $documentNumber = self::generateNumber($type);

            if ($type['affects_stock'] && !empty($data['items'])) {
                foreach ($data['items'] as $item) {
                    StockService::moveStock([
                        'item_id' => (int) $item['item_id'],
                        'warehouse_id' => (int) ($data['warehouse_id'] ?? $item['warehouse_id']),
                        'movement_type' => $typeCode,
                        'direction' => $type['direction'] === 'neutral' ? ($item['direction'] ?? 'in') : $type['direction'],
                        'quantity' => (string) $item['quantity'],
                        'unit_cost' => $item['unit_cost'] ?? null,
                        'reference_type' => 'movement_types',
                        'reference_id' => (int) $type['id'],
                        'notes' => $data['notes'] ?? $type['name_ar'],
                    ]);
                }
            }

            if ($type['posts_to_gl'] && !empty($specs['debit_account']) && !empty($specs['credit_account'])) {
                EventBus::dispatch('movement.post_gl', [
                    'movement_type' => $typeCode,
                    'debit_account' => $specs['debit_account'],
                    'credit_account' => $specs['credit_account'],
                    'amount' => $data['total_amount'] ?? '0.00',
                    'description' => $type['name_ar'] . ' - ' . $documentNumber,
                ]);
            }

            if ($type['creates_ar'] && !empty($data['member_id'])) {
                \App\Modules\Accounting\Services\AccountStatementService::recordCustomerTransaction([
                    'member_id' => (int) $data['member_id'],
                    'transaction_date' => $data['date'] ?? date('Y-m-d'),
                    'document_type' => 'sale',
                    'document_number' => $documentNumber,
                    'description' => $type['name_ar'],
                    'debit' => $data['total_amount'] ?? '0.00',
                ]);
            }

            if ($type['creates_ap'] && !empty($data['supplier_id'])) {
                \App\Modules\Accounting\Services\AccountStatementService::recordSupplierTransaction([
                    'supplier_id' => (int) $data['supplier_id'],
                    'transaction_date' => $data['date'] ?? date('Y-m-d'),
                    'document_type' => 'invoice',
                    'document_number' => $documentNumber,
                    'description' => $type['name_ar'],
                    'credit' => $data['total_amount'] ?? '0.00',
                ]);
            }

            $db->query(
                "UPDATE movement_types SET auto_number_next = auto_number_next + 1 WHERE id = ?",
                [(int) $type['id']]
            );

            $db->commit();

            EventBus::dispatch('movement.executed', [
                'type_code' => $typeCode,
                'document_number' => $documentNumber,
                'data' => $data,
            ]);

            return ['success' => true, 'document_number' => $documentNumber];
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    public static function getAllTypes(?string $module = null): array
    {
        $db = App::getInstance()->db();

        if ($module) {
            return $db->select(
                "SELECT * FROM movement_types WHERE module = ? AND is_active = 1 ORDER BY sort_order",
                [$module]
            );
        }

        return $db->select("SELECT * FROM movement_types WHERE is_active = 1 ORDER BY module, sort_order");
    }

    private static function generateNumber(array $type): string
    {
        $prefix = $type['auto_number_prefix'] ?? strtoupper(substr($type['code'], 0, 3));
        $next = (int) $type['auto_number_next'];
        return $prefix . '-' . date('Ymd') . '-' . str_pad((string) $next, 5, '0', STR_PAD_LEFT);
    }

    private static function castValue(?string $value, string $type): mixed
    {
        if ($value === null) return null;

        return match ($type) {
            'boolean' => $value === '1' || $value === 'true',
            'integer' => (int) $value,
            'decimal' => $value,
            'json' => json_decode($value, true),
            default => $value,
        };
    }
}
