<?php
namespace App\Services;

use App\Models\CommissionRule;
use App\Repositories\CommissionRuleRepository;
use App\Repositories\CommissionLedgerRepository;
use App\Utils\Logger;

class CommissionService
{
    private $ruleRepo;
    private $ledgerRepo;

    public function __construct(
        CommissionRuleRepository $ruleRepo,
        CommissionLedgerRepository $ledgerRepo
    ) {
        $this->ruleRepo = $ruleRepo;
        $this->ledgerRepo = $ledgerRepo;
    }

    public function getRules(array $filters = []): array
    {
        return $this->ruleRepo->getAll($filters);
    }

    public function saveRule(array $data): int
    {
        $ruleType = strtoupper(trim((string)($data['rule_type'] ?? 'TIERED_CASHFLOW')));
        $kpiSource = trim((string)($data['kpi_source'] ?? ''));
        if ($kpiSource === '' && $ruleType === 'TIERED_CASHFLOW') {
            // Tiered rules are not KPI-bound, but we keep a stable identifier for schema compatibility.
            $kpiSource = 'tiered_cashflow';
        }
        $targetUsersRaw = $data['target_users'] ?? [];
        $min = (float)($data['kpi_min_threshold'] ?? 0);
        $max = (float)($data['kpi_max_threshold'] ?? 0);
        $baseAmount = (float)($data['base_amount'] ?? 0);
        $rewardPercentage = (float)($data['reward_percentage'] ?? 0);
        $status = trim((string)($data['status'] ?? 'active'));
        $requestedServicesRaw = $data['requested_services'] ?? [];
        $tiersRaw = $data['tiers'] ?? '[]';

        if ($kpiSource === '') {
            throw new \InvalidArgumentException('منبع KPI الزامی است');
        }
        if ($max <= $min) {
            throw new \InvalidArgumentException('حداکثر آستانه باید از حداقل بزرگ‌تر باشد');
        }
        if ($rewardPercentage < 0 || $rewardPercentage > 100) {
            throw new \InvalidArgumentException('درصد پاداش باید بین 0 تا 100 باشد');
        }

        if (is_string($targetUsersRaw)) {
            $decoded = json_decode($targetUsersRaw, true);
            $targetUsers = is_array($decoded) ? $decoded : [];
        } elseif (is_array($targetUsersRaw)) {
            $targetUsers = $targetUsersRaw;
        } else {
            $targetUsers = [];
        }
        $salespersonUserId = isset($data['salesperson_user_id']) ? (int)$data['salesperson_user_id'] : 0;
        if ($salespersonUserId > 0) {
            $targetUsers = [$salespersonUserId];
        }
        if (empty($targetUsers)) {
            $targetUsers = ['ALL'];
        }
        if (is_string($requestedServicesRaw)) {
            $decodedServices = json_decode($requestedServicesRaw, true);
            $requestedServices = is_array($decodedServices) ? $decodedServices : [];
        } else {
            $requestedServices = is_array($requestedServicesRaw) ? $requestedServicesRaw : [];
        }
        if (empty($requestedServices)) {
            $requestedServices = ['ALL'];
        }

        if (is_string($tiersRaw)) {
            $decodedTiers = json_decode($tiersRaw, true);
            $tiers = is_array($decodedTiers) ? $decodedTiers : [];
        } else {
            $tiers = is_array($tiersRaw) ? $tiersRaw : [];
        }

        $rule = new CommissionRule([
            'id' => isset($data['id']) ? (int)$data['id'] : null,
            'kpi_source' => $kpiSource,
            'kpi_key' => $kpiSource,
            'target_users' => $targetUsers,
            'requested_services' => $requestedServices,
            'kpi_min_threshold' => $min,
            'kpi_max_threshold' => $max,
            'base_amount' => $baseAmount,
            'reward_percentage' => $rewardPercentage,
            'status' => in_array($status, ['active', 'inactive'], true) ? $status : 'active',
        ]);
        $pdo = $this->ruleRepo->getDb()->getPdo();
        try {
            $pdo->beginTransaction();
            $ruleId = $this->ruleRepo->save($rule);
            $this->saveRuleTiers($ruleId, $tiers, $ruleType);
            $pdo->commit();
            return $ruleId;
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }

    public function evaluateUserCommissions(int $userId, float $kpiScore, string $kpiSource): array
    {
        if ($userId <= 0) {
            throw new \InvalidArgumentException('شناسه کاربر نامعتبر است');
        }
        $kpiSource = trim($kpiSource);
        if ($kpiSource === '') {
            throw new \InvalidArgumentException('منبع KPI الزامی است');
        }

        $pdo = $this->ledgerRepo->getDb()->getPdo();
        $rules = $this->ruleRepo->findActiveByKpiSource($kpiSource);
        $results = [];

        try {
            $pdo->beginTransaction();
            foreach ($rules as $rule) {
                $targets = $rule->getTargetUsersArray();
                $appliesToAll = in_array('ALL', $targets, true);
                $appliesToUser = in_array($userId, array_map('intval', $targets), true);
                if (!$appliesToAll && !$appliesToUser) {
                    continue;
                }

                $min = (float)$rule->kpi_min_threshold;
                $max = (float)$rule->kpi_max_threshold;
                if ($kpiScore < $min || $kpiScore > $max) {
                    continue;
                }

                $baseAmount = (float)$rule->base_amount;
                $rewardPercentage = (float)$rule->reward_percentage;
                $finalAmount = round($baseAmount * ($rewardPercentage / 100), 2);

                $ledgerId = $this->ledgerRepo->store([
                    'user_id' => $userId,
                    'rule_id' => (int)$rule->id,
                    'kpi_score_recorded' => $kpiScore,
                    'calculated_amount' => $finalAmount,
                    'calculation_date' => date('Y-m-d H:i:s'),
                ]);

                $results[] = [
                    'ledger_id' => $ledgerId,
                    'rule_id' => (int)$rule->id,
                    'kpi_score_recorded' => $kpiScore,
                    'calculated_amount' => $finalAmount,
                ];
            }
            $pdo->commit();
            return $results;
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            Logger::logError('COMMISSION_EVALUATION_FAILED', $e, [
                'user_id' => $userId,
                'kpi_source' => $kpiSource,
                'kpi_score' => $kpiScore,
            ]);
            throw $e;
        }
    }

    public function getUserLedger(int $userId): array
    {
        return $this->ledgerRepo->getByUser($userId);
    }

    public function calculateCommissionTotal(array $rows, float $baseRatePercent, ?float $kpiMultiplier = null, float $deductionQc = 0.0): float
    {
        $rate = max(0.0, $baseRatePercent) / 100;
        $multiplier = $kpiMultiplier === null || $kpiMultiplier <= 0 ? 1.0 : $kpiMultiplier;
        $ncrSum = 0.0;

        foreach ($rows as $row) {
            $status = strtoupper(trim((string)($row['status'] ?? '')));
            if (!in_array($status, ['SETTLED', 'CLEARED'], true)) {
                continue;
            }
            $ncr = (float)($row['ncr'] ?? 0);
            if ($ncr <= 0) {
                continue;
            }
            $ncrSum += $ncr * $rate;
        }

        $total = ($ncrSum * $multiplier) - max(0.0, $deductionQc);
        return round($total, 2);
    }

    private function saveRuleTiers(int $ruleId, array $tiers, string $ruleType): void
    {
        if ($ruleId <= 0) {
            return;
        }
        $pdo = $this->ruleRepo->getDb()->getPdo();
        if (!$this->tableExists($pdo, 'commission_rule_tiers')) {
            return;
        }
        $ownsTransaction = !$pdo->inTransaction();
        if ($ownsTransaction) {
            $pdo->beginTransaction();
        }
        try {
            $pdo->prepare("DELETE FROM commission_rule_tiers WHERE rule_id = ?")->execute([$ruleId]);
            foreach ($tiers as $tier) {
                $commission = isset($tier['commission_percent']) ? (float)$tier['commission_percent'] : null;
                if ($commission === null) {
                    continue;
                }

                $columns = ['rule_id'];
                $values = [$ruleId];

                if ($ruleType === 'TIERED_CASHFLOW') {
                    $minAmount = isset($tier['min_amount']) && $tier['min_amount'] !== '' ? (float)$tier['min_amount'] : null;
                    $maxAmount = isset($tier['max_amount']) && $tier['max_amount'] !== '' ? (float)$tier['max_amount'] : null;
                    if ($minAmount === null) {
                        continue;
                    }
                    if ($this->columnExists($pdo, 'commission_rule_tiers', 'min_amount')) {
                        $columns[] = 'min_amount';
                        $values[] = $minAmount;
                    } elseif ($this->columnExists($pdo, 'commission_rule_tiers', 'min_realization_percent')) {
                        $columns[] = 'min_realization_percent';
                        $values[] = $minAmount;
                    }

                    if ($maxAmount !== null) {
                        if ($this->columnExists($pdo, 'commission_rule_tiers', 'max_amount')) {
                            $columns[] = 'max_amount';
                            $values[] = $maxAmount;
                        } elseif ($this->columnExists($pdo, 'commission_rule_tiers', 'max_realization_percent')) {
                            $columns[] = 'max_realization_percent';
                            $values[] = $maxAmount;
                        }
                    }
                } else {
                    $min = isset($tier['min_realization']) && $tier['min_realization'] !== '' ? (float)$tier['min_realization'] : null;
                    $max = isset($tier['max_realization']) && $tier['max_realization'] !== '' ? (float)$tier['max_realization'] : null;
                    if ($min === null || $max === null) {
                        continue;
                    }
                    if ($this->columnExists($pdo, 'commission_rule_tiers', 'min_realization_percent')) {
                        $columns[] = 'min_realization_percent';
                        $values[] = $min;
                    }
                    if ($this->columnExists($pdo, 'commission_rule_tiers', 'max_realization_percent')) {
                        $columns[] = 'max_realization_percent';
                        $values[] = $max;
                    }
                }

                if ($this->columnExists($pdo, 'commission_rule_tiers', 'allocated_commission_percent')) {
                    $columns[] = 'allocated_commission_percent';
                    $values[] = $commission;
                } elseif ($this->columnExists($pdo, 'commission_rule_tiers', 'commission_percent')) {
                    $columns[] = 'commission_percent';
                    $values[] = $commission;
                }

                if (count($columns) <= 1) {
                    continue;
                }

                $placeholders = implode(',', array_fill(0, count($columns), '?'));
                $sql = "INSERT INTO commission_rule_tiers (" . implode(',', $columns) . ") VALUES (" . $placeholders . ")";
                $stmt = $pdo->prepare($sql);
                $stmt->execute($values);
            }
            if ($ownsTransaction && $pdo->inTransaction()) {
                $pdo->commit();
            }
        } catch (\Throwable $e) {
            if ($ownsTransaction && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            Logger::logError('COMMISSION_TIER_SAVE_FAILED', $e, [
                'rule_id' => $ruleId,
                'rule_type' => $ruleType,
                'tiers' => $tiers,
            ]);
            throw $e;
        }
    }

    private function tableExists(\PDO $pdo, string $tableName): bool
    {
        $stmt = $pdo->prepare("
            SELECT COUNT(*) AS cnt
            FROM information_schema.TABLES
            WHERE TABLE_SCHEMA = DATABASE()
              AND TABLE_NAME = ?
        ");
        $stmt->execute([$tableName]);
        return ((int)($stmt->fetch(\PDO::FETCH_ASSOC)['cnt'] ?? 0)) > 0;
    }

    private function columnExists(\PDO $pdo, string $tableName, string $column): bool
    {
        $stmt = $pdo->prepare("
            SELECT COUNT(*) AS cnt
            FROM information_schema.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE()
              AND TABLE_NAME = ?
              AND COLUMN_NAME = ?
        ");
        $stmt->execute([$tableName, $column]);
        return ((int)($stmt->fetch(\PDO::FETCH_ASSOC)['cnt'] ?? 0)) > 0;
    }

    // Compatibility no-op methods to avoid breaking existing call sites during migration window.
    public function calculateAndStoreForWonDeal(string $dealDidarId)
    {
        return null;
    }

    public function calculateAndCreateForDeal(string $dealDidarId)
    {
        return null;
    }
}
