<?php
namespace App\Services;

use App\Models\CommissionRule;
use App\Models\DealCommission;
use App\Repositories\CommissionRuleRepository;
use App\Repositories\DealCommissionRepository;
use App\Repositories\DealRepository;
use App\Repositories\ProductRepository;
use App\Repositories\UserRepository;
use App\Repositories\ActivityRepository;
use App\Repositories\PersonRepository;

class CommissionService
{
    private $ruleRepo;
    private $dealCommissionRepo;
    private $dealRepo;
    private $productRepo;
    private $userRepo;
    private $activityRepo;
    private $personRepo;

    public function __construct(
        CommissionRuleRepository $ruleRepo,
        DealCommissionRepository $dealCommissionRepo,
        DealRepository $dealRepo,
        ProductRepository $productRepo,
        UserRepository $userRepo,
        ActivityRepository $activityRepo,
        PersonRepository $personRepo
    ) {
        $this->ruleRepo = $ruleRepo;
        $this->dealCommissionRepo = $dealCommissionRepo;
        $this->dealRepo = $dealRepo;
        $this->productRepo = $productRepo;
        $this->userRepo = $userRepo;
        $this->activityRepo = $activityRepo;
        $this->personRepo = $personRepo;
    }

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

    public function saveRule(array $data, int $actorUserId = 0): int
    {
        $salespersonUserId = (int)($data['salesperson_user_id'] ?? 0);
        $productId = (int)($data['product_id'] ?? 0);
        $baseRate = (float)($data['base_rate_percent'] ?? 0);
        $validFrom = trim((string)($data['valid_from'] ?? ''));
        $validTo = trim((string)($data['valid_to'] ?? ''));
        $isActive = isset($data['is_active']) ? (int)$data['is_active'] : 1;

        if ($salespersonUserId <= 0 || $productId <= 0 || $baseRate <= 0 || $validFrom === '') {
            throw new \InvalidArgumentException('اطلاعات قانون پورسانت کامل نیست');
        }

        $rule = new CommissionRule([
            'id' => isset($data['id']) ? (int)$data['id'] : null,
            'salesperson_user_id' => $salespersonUserId,
            'product_id' => $productId,
            'base_rate_percent' => $baseRate,
            'valid_from' => $validFrom,
            'valid_to' => $validTo !== '' ? $validTo : null,
            'is_active' => $isActive,
            'created_by_user_id' => $actorUserId > 0 ? $actorUserId : null,
        ]);
        return $this->ruleRepo->save($rule);
    }

    public function calculateAndStoreForWonDeal(string $dealDidarId): ?DealCommission
    {
        $deal = $this->dealRepo->findByDidarId($dealDidarId);
        if (!$deal || trim((string)$deal->status) !== 'Won') {
            return null;
        }

        $salesperson = $this->userRepo->findByDidarId((string)$deal->owner_didar_id);
        if (!$salesperson || (int)$salesperson->id <= 0) {
            return null;
        }

        $productId = $this->resolvePrimaryProductId((string)$deal->didar_deal_id);
        if ($productId <= 0) {
            return null;
        }

        $ruleBasisDate = $this->resolveRuleBasisDateForDeal($deal);
        $rule = $this->ruleRepo->findApplicableRule((int)$salesperson->id, $productId, $ruleBasisDate);
        if (!$rule) {
            return null;
        }

        $ncr = (float)($deal->payment_amount ?? 0);
        $kpiMultiplier = $this->calculateKpiMultiplier((string)$deal->didar_deal_id);
        $rate = (float)$rule->base_rate_percent;
        $calculated = round(($ncr * ($rate / 100)) * $kpiMultiplier, 2);

        $commission = new DealCommission([
            'deal_didar_id' => (string)$deal->didar_deal_id,
            'salesperson_user_id' => (int)$salesperson->id,
            'product_id' => $productId,
            'commission_rule_id' => (int)$rule->id,
            'ncr_amount' => $ncr,
            'applied_rate_percent' => $rate,
            'kpi_multiplier' => $kpiMultiplier,
            'calculated_commission' => $calculated,
            'status' => 'PENDING_MANAGER_REVIEW',
        ]);
        $id = $this->dealCommissionRepo->save($commission);
        if ($id <= 0) {
            return null;
        }
        return $this->dealCommissionRepo->findByDealDidarId((string)$deal->didar_deal_id);
    }

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

    public function getDealCommissionByDealDidarId(string $dealDidarId): ?DealCommission
    {
        return $this->dealCommissionRepo->findByDealDidarId(trim($dealDidarId));
    }

    public function advanceStatus(int $commissionId, string $status, int $actorUserId = 0): bool
    {
        $allowed = ['PENDING_MANAGER_REVIEW', 'PENDING_FINANCE', 'READY_FOR_PAYOUT', 'SETTLED'];
        $status = strtoupper(trim($status));
        if (!in_array($status, $allowed, true)) {
            throw new \InvalidArgumentException('وضعیت نامعتبر است');
        }
        return $this->dealCommissionRepo->updateStatus($commissionId, $status, $actorUserId);
    }

    private function resolvePrimaryProductId(string $dealDidarId): int
    {
        $rows = $this->dealRepo->getProductsByDealDidarId($dealDidarId);
        if (!empty($rows) && isset($rows[0]['product_id'])) {
            return (int)$rows[0]['product_id'];
        }
        return 0;
    }

    private function calculateKpiMultiplier(string $dealDidarId): float
    {
        $activities = $this->activityRepo->findByDealId($dealDidarId, 500);
        $count = is_array($activities) ? count($activities) : 0;
        if ($count >= 8) return 1.20;
        if ($count >= 5) return 1.10;
        if ($count >= 2) return 1.00;
        return 0.90;
    }

    public function buildLeadCommissionPreview(array $lead): array
    {
        $ownerDidarId = trim((string)($lead['owner_didar_id'] ?? ''));
        if ($ownerDidarId === '') {
            return ['matched' => false];
        }

        $salesperson = $this->userRepo->findByDidarId($ownerDidarId);
        if (!$salesperson || (int)$salesperson->id <= 0) {
            return ['matched' => false];
        }

        $productId = $this->resolveLeadPrimaryProductId($lead);
        if ($productId <= 0) {
            return ['matched' => false];
        }

        $basisDate = $this->resolveLeadRegisterDate($lead) ?: date('Y-m-d');
        $rule = $this->ruleRepo->findApplicableRule((int)$salesperson->id, $productId, $basisDate);
        if (!$rule) {
            return ['matched' => false];
        }

        $kpiMultiplier = $this->calculateLeadKpiMultiplier((string)($lead['didar_contact_id'] ?? ''));
        $ncr = $this->extractLeadExpectedNcr($lead);
        $estimated = $ncr > 0 ? round(($ncr * ((float)$rule->base_rate_percent / 100)) * $kpiMultiplier, 2) : null;

        return [
            'matched' => true,
            'commission_rate_percent' => (float)$rule->base_rate_percent,
            'commission_kpi_multiplier' => $kpiMultiplier,
            'commission_ncr_basis' => $ncr,
            'commission_estimated_amount' => $estimated,
            'commission_rule_valid_from' => (string)$rule->valid_from,
            'commission_rule_valid_to' => (string)($rule->valid_to ?? ''),
            'commission_basis_date' => $basisDate,
            'commission_rule_id' => (int)$rule->id,
        ];
    }

    private function resolveRuleBasisDateForDeal($deal): string
    {
        $fallback = !empty($deal->won_time) ? substr((string)$deal->won_time, 0, 10) : date('Y-m-d');
        $contactDidarId = trim((string)($deal->contact_didar_id ?? ''));
        if ($contactDidarId === '') {
            return $fallback;
        }

        $person = $this->personRepo->findByDidarId($contactDidarId);
        if (!$person) {
            return $fallback;
        }

        $registerTime = trim((string)($person->register_time ?? ''));
        if ($registerTime === '') {
            return $fallback;
        }
        return substr($registerTime, 0, 10);
    }

    private function resolveLeadPrimaryProductId(array $lead): int
    {
        $didarContactId = trim((string)($lead['didar_contact_id'] ?? ''));
        if ($didarContactId === '') {
            return 0;
        }
        $deals = $this->dealRepo->findByContactId($didarContactId);
        if (empty($deals)) {
            return 0;
        }
        $latestDeal = $deals[0];
        $dealDidarId = trim((string)($latestDeal->didar_deal_id ?? ''));
        if ($dealDidarId === '') {
            return 0;
        }
        return $this->resolvePrimaryProductId($dealDidarId);
    }

    private function resolveLeadRegisterDate(array $lead): ?string
    {
        $registerTime = trim((string)($lead['register_time'] ?? ''));
        if ($registerTime !== '') {
            return substr($registerTime, 0, 10);
        }
        return null;
    }

    private function calculateLeadKpiMultiplier(string $contactDidarId): float
    {
        if ($contactDidarId === '') {
            return 0.90;
        }
        $activities = $this->activityRepo->findByContactId($contactDidarId, 500);
        $count = is_array($activities) ? count($activities) : 0;
        if ($count >= 8) return 1.20;
        if ($count >= 5) return 1.10;
        if ($count >= 2) return 1.00;
        return 0.90;
    }

    private function extractLeadExpectedNcr(array $lead): float
    {
        $didarContactId = trim((string)($lead['didar_contact_id'] ?? ''));
        if ($didarContactId === '') {
            return 0.0;
        }
        $deals = $this->dealRepo->findByContactId($didarContactId);
        if (empty($deals)) {
            return 0.0;
        }
        $latestDeal = $deals[0];
        return (float)($latestDeal->payment_amount ?? 0);
    }

    public function calculateRepCommissionKpis(array $filters): array
    {
        $dateFrom = trim((string)($filters['date_from'] ?? date('Y-m-01')));
        $dateTo = trim((string)($filters['date_to'] ?? date('Y-m-t')));
        $ownerDidarId = trim((string)($filters['owner_didar_id'] ?? ($_SESSION['didar_id'] ?? '')));

        $salesperson = $this->userRepo->findByDidarId($ownerDidarId);
        if (!$salesperson || (int)$salesperson->id <= 0) {
            return ['tce' => 0.0, 'pcp' => 0.0, 'kma' => 0.0];
        }
        $salespersonUserId = (int)$salesperson->id;

        $settledRows = $this->dealCommissionRepo->getAll(['salesperson_user_id' => $salespersonUserId, 'status' => 'SETTLED']);
        $tce = 0.0;
        $kpiSum = 0.0;
        $kpiCount = 0;
        foreach ($settledRows as $row) {
            $ts = trim((string)($row->settled_at ?: $row->updated_at ?: $row->created_at));
            if ($ts !== '') {
                $date = substr($ts, 0, 10);
                if ($date < $dateFrom || $date > $dateTo) {
                    continue;
                }
            }
            $tce += (float)$row->calculated_commission;
            $kpiSum += (float)$row->kpi_multiplier;
            $kpiCount++;
        }

        $pendingDeals = $this->dealRepo->findByOwnerDidarId($ownerDidarId);
        $pcp = 0.0;
        foreach ($pendingDeals as $deal) {
            $status = trim((string)($deal->status ?? ''));
            if (in_array($status, ['Won', 'Lost'], true)) {
                continue;
            }
            $dealDate = substr((string)($deal->register_time ?? date('Y-m-d')), 0, 10);
            if ($dealDate < $dateFrom || $dealDate > $dateTo) {
                continue;
            }
            $productId = $this->resolvePrimaryProductId((string)$deal->didar_deal_id);
            if ($productId <= 0) {
                continue;
            }
            $basisDate = $this->resolveRuleBasisDateForDeal($deal);
            $rule = $this->ruleRepo->findApplicableRule($salespersonUserId, $productId, $basisDate);
            if (!$rule) {
                continue;
            }
            $ncr = (float)($deal->payment_amount ?? $deal->payable_amount ?? 0);
            $kpi = $this->calculateKpiMultiplier((string)$deal->didar_deal_id);
            $pcp += ($ncr * ((float)$rule->base_rate_percent / 100)) * $kpi;
        }

        return [
            'tce' => round($tce, 2),
            'pcp' => round($pcp, 2),
            'kma' => round($kpiCount > 0 ? ($kpiSum / $kpiCount) : 0, 4),
        ];
    }

    public function calculateManagerCommissionKpis(array $filters): array
    {
        $dateFrom = trim((string)($filters['date_from'] ?? date('Y-m-01')));
        $dateTo = trim((string)($filters['date_to'] ?? date('Y-m-t')));

        $settledRows = $this->dealCommissionRepo->getAll(['status' => 'SETTLED']);
        $tcp = 0.0;
        $teamNcr = 0.0;
        foreach ($settledRows as $row) {
            $ts = trim((string)($row->settled_at ?: $row->updated_at ?: $row->created_at));
            if ($ts !== '') {
                $date = substr($ts, 0, 10);
                if ($date < $dateFrom || $date > $dateTo) {
                    continue;
                }
            }
            $tcp += (float)$row->calculated_commission;
            $teamNcr += (float)$row->ncr_amount;
        }

        $allDeals = $this->dealRepo->getAll(['register_date_from' => $dateFrom, 'register_date_to' => $dateTo]);
        $atRiskAmount = 0.0;
        foreach ($allDeals as $deal) {
            $status = trim((string)($deal->status ?? ''));
            $pipelineStage = trim((string)($deal->pipeline_stage ?? ''));
            if ($status === 'Won' || $status === 'Lost') {
                continue;
            }
            if (!in_array($pipelineStage, ['تماس عدم پاسخ', 'تماس پیگیری', 'پیگیری مانده حساب'], true)) {
                continue;
            }
            $atRiskAmount += (float)($deal->payable_amount ?? $deal->estimated_price ?? 0);
        }

        $roci = $tcp > 0 ? ($teamNcr / $tcp) : 0.0;
        $ecr = $teamNcr > 0 ? (($tcp / $teamNcr) * 100.0) : 0.0;

        return [
            'tcp' => round($tcp, 2),
            'roci' => round($roci, 4),
            'ecr' => round($ecr, 4),
            'cpar' => round($atRiskAmount, 2),
        ];
    }
}
