<?php
namespace App\Services;

use App\Models\CommissionPayoutCycle;
use App\Repositories\CommissionPayoutCycleRepository;
use App\Repositories\UserRepository;

class PayrollIntegrationService
{
    private $cycleRepo;
    private $userRepo;

    public function __construct(
        CommissionPayoutCycleRepository $cycleRepo,
        UserRepository $userRepo
    ) {
        $this->cycleRepo = $cycleRepo;
        $this->userRepo = $userRepo;
    }

    public function aggregateMonthlySettledCommissions(string $billingMonth, int $managerId = 0, int $payrollDay = 30): array
    {
        $billingMonth = trim($billingMonth);
        if (!preg_match('/^\d{4}\-\d{2}$/', $billingMonth)) {
            throw new \InvalidArgumentException('فرمت ماه نامعتبر است');
        }

        $rows = $this->cycleRepo->aggregateSettledByMonth($billingMonth);
        $results = [];
        foreach ($rows as $row) {
            $userId = (int)($row['user_id'] ?? 0);
            if ($userId <= 0) {
                continue;
            }
            $calculated = round((float)($row['calculated_amount'] ?? 0), 2);
            $approvalDeadline = $this->buildApprovalDeadline($billingMonth, $payrollDay);
            $cycle = $this->cycleRepo->findByUserAndMonth($userId, $billingMonth);
            $existingStatus = strtoupper(trim((string)($cycle->status ?? '')));

            if ($cycle && in_array($existingStatus, ['APPROVED', 'OVERRIDDEN', 'REJECTED', 'TRANSFERRED_TO_PAYROLL'], true)) {
                $results[] = $cycle->toArray();
                continue;
            }

            $entity = new CommissionPayoutCycle([
                'id' => $cycle ? (int)$cycle->id : null,
                'user_id' => $userId,
                'billing_month' => $billingMonth,
                'calculated_amount' => $calculated,
                'override_amount' => $cycle ? $cycle->override_amount : null,
                'final_payable_amount' => $cycle ? (float)$cycle->final_payable_amount : $calculated,
                'manager_id' => $managerId > 0 ? $managerId : ($cycle ? (int)$cycle->manager_id : null),
                'status' => 'PENDING_APPROVAL',
                'override_reason' => $cycle ? $cycle->override_reason : null,
                'approval_deadline' => $approvalDeadline,
                'escalate_to_finance' => 0,
            ]);
            $id = $this->cycleRepo->save($entity);
            $saved = $this->cycleRepo->findById($id);
            if ($saved) {
                $results[] = $saved->toArray();
            }
        }
        return $results;
    }

    public function processDeadlineSla(string $billingMonth = ''): array
    {
        $queue = $this->cycleRepo->getManagerQueue($billingMonth, 'PENDING_APPROVAL');
        $now = date('Y-m-d H:i:s');
        $updated = [];
        foreach ($queue as $cycle) {
            $deadline = trim((string)($cycle->approval_deadline ?? ''));
            if ($deadline === '' || $deadline >= $now) {
                continue;
            }
            $cycle->status = 'TRANSFERRED_TO_PAYROLL';
            $cycle->final_payable_amount = (float)$cycle->calculated_amount;
            $cycle->override_amount = null;
            $cycle->override_reason = 'SLA deadline missed -> escalated to finance';
            $cycle->escalate_to_finance = 1;
            $this->cycleRepo->save($cycle);
            $updated[] = $cycle->toArray();
        }
        return $updated;
    }

    public function getManagerQueue(string $billingMonth = '', bool $applySla = true): array
    {
        if ($applySla) {
            $this->processDeadlineSla($billingMonth);
        }
        return array_map(static function ($cycle) {
            return $cycle->toArray();
        }, $this->cycleRepo->getManagerQueue($billingMonth, 'PENDING_APPROVAL'));
    }

    public function decidePayoutCycle(int $cycleId, string $action, int $managerId, ?float $overrideAmount = null, string $reason = ''): CommissionPayoutCycle
    {
        if ($cycleId <= 0 || $managerId <= 0) {
            throw new \InvalidArgumentException('شناسه چرخه یا مدیر نامعتبر است');
        }

        $action = strtoupper(trim($action));
        if (!in_array($action, ['APPROVE', 'OVERRIDE', 'REJECT'], true)) {
            throw new \InvalidArgumentException('اقدام نامعتبر است');
        }

        $pdo = $this->cycleRepo->getDb()->getPdo();
        $pdo->beginTransaction();
        try {
            $cycle = $this->cycleRepo->findById($cycleId);
            if (!$cycle) {
                throw new \RuntimeException('چرخه پورسانت یافت نشد');
            }
            if (strtoupper(trim((string)$cycle->status)) !== 'PENDING_APPROVAL') {
                throw new \RuntimeException('این چرخه دیگر قابل تصمیم‌گیری نیست');
            }

            $cycle->manager_id = $managerId;

            if ($action === 'APPROVE') {
                $cycle->status = 'APPROVED';
                $cycle->override_amount = null;
                $cycle->final_payable_amount = (float)$cycle->calculated_amount;
                $cycle->override_reason = '';
                $cycle->escalate_to_finance = 0;
            } elseif ($action === 'OVERRIDE') {
                if ($overrideAmount === null || $overrideAmount < 0) {
                    throw new \InvalidArgumentException('مبلغ Override نامعتبر است');
                }
                if (trim($reason) === '') {
                    throw new \InvalidArgumentException('علت Override الزامی است');
                }
                $cycle->status = 'OVERRIDDEN';
                $cycle->override_amount = round((float)$overrideAmount, 2);
                $cycle->final_payable_amount = (float)$cycle->override_amount;
                $cycle->override_reason = trim($reason);
                $cycle->escalate_to_finance = 0;
            } else {
                if (trim($reason) === '') {
                    throw new \InvalidArgumentException('علت رد الزامی است');
                }
                $cycle->status = 'REJECTED';
                $cycle->override_amount = null;
                $cycle->final_payable_amount = 0;
                $cycle->override_reason = trim($reason);
                $cycle->escalate_to_finance = 0;
            }

            $this->cycleRepo->save($cycle);
            $pdo->commit();
            $saved = $this->cycleRepo->findById($cycleId);
            if (!$saved) {
                throw new \RuntimeException('خطا در بازیابی چرخه ذخیره‌شده');
            }
            return $saved;
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }

    private function buildApprovalDeadline(string $billingMonth, int $payrollDay): string
    {
        $payrollDay = max(1, min(31, $payrollDay));
        $year = (int)substr($billingMonth, 0, 4);
        $month = (int)substr($billingMonth, 5, 2);
        $daysInMonth = (int)date('t', strtotime(sprintf('%04d-%02d-01', $year, $month)));
        $effectivePayrollDay = min($payrollDay, $daysInMonth);
        $payrollDate = sprintf('%04d-%02d-%02d', $year, $month, $effectivePayrollDay);
        $deadline = date('Y-m-d', strtotime($payrollDate . ' -2 days'));
        return $deadline . ' 23:59:59';
    }
}
