<?php
declare(strict_types=1);

namespace App\Modules\Rules\Services;

use App\Core\App;
use App\Core\Logger;
use App\Modules\Rules\Models\BusinessRule;
use App\Modules\Rules\Models\RuleVersion;
use App\Modules\Rules\Models\RuleOverride;

final class RuleEngine
{
    private static array $cache = [];

    public static function get(string $ruleCode, ?int $branchId = null, ?string $date = null): mixed
    {
        $date = $date ?? date('Y-m-d');
        $cacheKey = "{$ruleCode}_{$branchId}_{$date}";

        if (isset(self::$cache[$cacheKey])) {
            return self::$cache[$cacheKey];
        }

        $db = App::getInstance()->db();

        // Try branch-specific first
        $rule = null;
        if ($branchId !== null) {
            $rule = $db->selectOne(
                "SELECT * FROM business_rules WHERE rule_code = ? AND branch_id = ? AND is_active = 1 AND effective_from <= ? AND (effective_to IS NULL OR effective_to >= ?)",
                [$ruleCode, $branchId, $date, $date]
            );
        }

        // Fallback to global
        if (!$rule) {
            $rule = $db->selectOne(
                "SELECT * FROM business_rules WHERE rule_code = ? AND branch_id IS NULL AND is_active = 1 AND effective_from <= ? AND (effective_to IS NULL OR effective_to >= ?)",
                [$ruleCode, $date, $date]
            );
        }

        if (!$rule) {
            Logger::warning("Rule not found: {$ruleCode}", ['branch_id' => $branchId, 'date' => $date]);
            self::$cache[$cacheKey] = null;
            return null;
        }

        $value = json_decode($rule['current_value_json'], true);
        self::$cache[$cacheKey] = $value;
        return $value;
    }

    public static function getValue(string $ruleCode, string $key = 'value', ?int $branchId = null): mixed
    {
        $data = self::get($ruleCode, $branchId);
        if ($data === null) {
            return null;
        }
        return $data[$key] ?? ($data['percentage'] ?? ($data['amount'] ?? ($data['value'] ?? null)));
    }

    public static function getAll(string $category, ?int $branchId = null): array
    {
        return BusinessRule::allByCategory($category, $branchId);
    }

    public static function update(string $ruleCode, string $newValueJson, string $reason, ?int $branchId = null): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $where = 'rule_code = ? AND is_active = 1';
        $params = [$ruleCode];
        if ($branchId !== null) {
            $where .= ' AND branch_id = ?';
            $params[] = $branchId;
        } else {
            $where .= ' AND branch_id IS NULL';
        }

        $rule = $db->selectOne("SELECT * FROM business_rules WHERE {$where}", $params);
        if (!$rule) {
            throw new \RuntimeException("Rule not found: {$ruleCode}");
        }

        $newVersion = (int) $rule['version'] + 1;

        // Create version history
        $db->insert('rule_versions', [
            'rule_id'        => (int) $rule['id'],
            'version_number' => $newVersion,
            'old_value_json' => $rule['current_value_json'],
            'new_value_json' => $newValueJson,
            'changed_by'     => $employee ? (int) $employee->id : null,
            'changed_at'     => date('Y-m-d H:i:s'),
            'change_reason'  => $reason,
        ]);

        // Update current value
        $db->update('business_rules', [
            'current_value_json' => $newValueJson,
            'version'            => $newVersion,
            'updated_at'         => date('Y-m-d H:i:s'),
            'updated_by'         => $employee ? (int) $employee->id : null,
        ], '`id` = ?', [$rule['id']]);

        // Clear cache
        self::$cache = [];

        Logger::info("Rule updated: {$ruleCode}", ['version' => $newVersion, 'reason' => $reason]);
    }

    public static function override(string $ruleCode, string $entityType, int $entityId, string $overrideValueJson, string $reason): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $rule = $db->selectOne("SELECT id FROM business_rules WHERE rule_code = ? AND branch_id IS NULL AND is_active = 1", [$ruleCode]);
        if (!$rule) {
            throw new \RuntimeException("Rule not found: {$ruleCode}");
        }

        $db->insert('rule_overrides', [
            'rule_id'             => (int) $rule['id'],
            'entity_type'         => $entityType,
            'entity_id'           => $entityId,
            'override_value_json' => $overrideValueJson,
            'reason'              => $reason,
            'approved_by'         => $employee ? (int) $employee->id : null,
            'effective_from'      => date('Y-m-d'),
            'is_active'           => 1,
            'created_at'          => date('Y-m-d H:i:s'),
        ]);
    }

    public static function getEffective(string $ruleCode, string $entityType, int $entityId, ?int $branchId = null): mixed
    {
        $db = App::getInstance()->db();
        $rule = $db->selectOne("SELECT id FROM business_rules WHERE rule_code = ? AND branch_id IS NULL AND is_active = 1", [$ruleCode]);
        if ($rule) {
            $override = RuleOverride::findOverride((int) $rule['id'], $entityType, $entityId);
            if ($override) {
                return json_decode($override['override_value_json'], true);
            }
        }
        return self::get($ruleCode, $branchId);
    }

    public static function require(string $ruleCode, ?int $branchId = null): array
    {
        $data = self::get($ruleCode, $branchId);
        if ($data === null) {
            Logger::error("Missing required business rule: {$ruleCode}", ['branch_id' => $branchId]);
            throw new \RuntimeException("القاعدة '{$ruleCode}' غير مُعرّفة في النظام — يرجى مراجعة إعدادات القواعد");
        }
        return $data;
    }

    public static function requireValue(string $ruleCode, string $key = 'value', ?int $branchId = null): mixed
    {
        $data = self::require($ruleCode, $branchId);
        return $data[$key] ?? ($data['percentage'] ?? ($data['amount'] ?? ($data['value'] ?? null)));
    }

    public static function clearCache(): void
    {
        self::$cache = [];
    }
}