<?php
namespace App\Controllers;

use App\Services\CommissionService;
use App\Services\AuthService;
use App\Repositories\UserRepository;
use App\Controllers\Concerns\BulkDeleteRequest;
use App\Utils\DateNormalizer;
use App\Utils\Sanitizer;
use App\Utils\Logger;

class CommissionController extends ApiController
{
    use BulkDeleteRequest;

    private $commissionService;
    private $authService;
    private $userRepo;

    public function __construct(
        CommissionService $commissionService,
        AuthService $authService,
        UserRepository $userRepo
    ) {
        $this->commissionService = $commissionService;
        $this->authService = $authService;
        $this->userRepo = $userRepo;
    }

    public function getRules()
    {
        $selfOnly = (($_SESSION['role'] ?? '') === 'agent') && !$this->authService->isAdminOrCrmSpecialist();
        if (!$selfOnly) {
            $this->assertAdminOrCrm('commissions.view');
        }
        $rules = array_map(function ($rule) use ($selfOnly) {
            $payload = $rule->toArray();
            $ruleTypeRaw = strtoupper(trim((string)($payload['rule_type'] ?? '')));
            if ($ruleTypeRaw === '') {
                $legacyType = strtoupper(trim((string)($payload['type'] ?? '')));
                if ($legacyType !== '') {
                    $ruleTypeRaw = $legacyType;
                }
            }
            if ($ruleTypeRaw === '') {
                $ruleTypeRaw = !empty($payload['base_amount']) ? 'FIXED_RULE' : 'TIERED_RULE';
            }
            $payload['rule_type'] = $ruleTypeRaw;
            if ($selfOnly) {
                $payload['target_users'] = [];
            }
            $salespersonId = (int)($payload['salesperson_user_id'] ?? 0);
            if ($salespersonId > 0) {
                $u = $this->userRepo->findById($salespersonId);
                if ($u) {
                    $payload['salesperson'] = [
                        'id' => (int)$u->id,
                        'first_name' => (string)($u->first_name ?? ''),
                        'last_name' => (string)($u->last_name ?? ''),
                        'display_name' => (string)($u->display_name ?? ''),
                        'role' => (string)($u->role ?? ''),
                    ];
                }
            } else {
                $payload['salesperson'] = null;
            }
            $payload['tiers'] = $this->commissionService->getRuleTiers((int)($payload['id'] ?? 0));
            return $payload;
        }, $this->commissionService->getRules());
        $assignments = $selfOnly
            ? $this->commissionService->getRuleAssignments(['salesperson_user_id' => (int)($_SESSION['user_id'] ?? 0)])
            : $this->commissionService->getRuleAssignments();
        $this->successResponse(['rules' => $rules, 'assignments' => $assignments]);
    }

    public function storeRule()
    {
        $this->assertAdminOrCrm('commissions.rules.manage');
        error_log('[store_commission_rule] payload=' . print_r($_POST, true));

        $targetUsers = $_POST['target_users'] ?? '[]';
        $salespersonRaw = $_POST['salesperson_user_id'] ?? null;
        $salespersonId = null;
        if ($salespersonRaw !== null && $salespersonRaw !== '') {
            $salespersonId = (int)$salespersonRaw;
            if ($salespersonId <= 0) {
                $salespersonId = null;
            }
        }
        $payload = [
            'id' => (int)($_POST['id'] ?? 0),
            'rule_type' => Sanitizer::sanitize($_POST['rule_type'] ?? 'TIERED_RULE'),
            'target_users' => $targetUsers,
            'salesperson_user_id' => $salespersonId,
            'requested_services' => $_POST['requested_services'] ?? '[]',
            'base_amount' => (float)($_POST['base_amount'] ?? 0),
            'reward_percentage' => (float)($_POST['reward_percentage'] ?? 0),
            'tiers' => $_POST['tiers'] ?? '[]',
            'title' => $_POST['title'] ?? '',
            'valid_from' => $_POST['valid_from'] ?? '',
            'valid_to' => $_POST['valid_to'] ?? '',
        ];

        try {
            $id = $this->commissionService->saveRule($payload);
            $this->successResponse(['id' => $id], 'قانون کمیسیون ذخیره شد');
        } catch (\PDOException $e) {
            $message = (string)$e->getMessage();
            error_log('[store_commission_rule][pdo_exception] ' . $message);
            Logger::logError('STORE_COMMISSION_RULE_PDO', $e, ['post' => $_POST]);
            $isFkViolation = strpos($message, 'Integrity constraint violation') !== false
                || strpos($message, 'SQLSTATE[23000]') !== false;
            $isSalespersonFk = strpos($message, 'commission_rules_ibfk_1') !== false
                || strpos($message, 'salesperson_user_id') !== false;
            $isProductFk = strpos($message, 'commission_rules_ibfk_2') !== false
                || strpos($message, 'product_id') !== false;
            if ($isFkViolation && $isSalespersonFk) {
                $this->errorResponse('کاربر انتخاب شده نامعتبر است', 422);
            }
            if ($isFkViolation && $isProductFk) {
                $this->errorResponse('محصول انتخاب شده نامعتبر است', 422);
            }
            $this->errorResponse('خطای پایگاه داده در ذخیره قانون پورسانت', 500);
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            Logger::logError('STORE_COMMISSION_RULE_UNKNOWN', $e, ['post' => $_POST]);
            $this->errorResponse('خطا در ذخیره قانون پورسانت', 500);
        }
    }

    public function getDealCommissions()
    {
        $selfOnly = (($_SESSION['role'] ?? '') === 'agent') && !$this->authService->isAdminOrCrmSpecialist();
        if (!$selfOnly) {
            $this->assertAdminOrCrm('commissions.view');
        }
        $requestedOwnerDidarId = Sanitizer::sanitize($_POST['owner_didar_id'] ?? $_GET['owner_didar_id'] ?? '');
        $filters = [
            'status' => Sanitizer::sanitize($_POST['status'] ?? $_GET['status'] ?? ''),
            'salesperson_user_id' => (int)($_POST['salesperson_user_id'] ?? $_GET['salesperson_user_id'] ?? 0),
            'owner_didar_id' => $requestedOwnerDidarId,
            'date_from' => Sanitizer::sanitize($_POST['date_from'] ?? $_GET['date_from'] ?? ''),
            'date_to' => Sanitizer::sanitize($_POST['date_to'] ?? $_GET['date_to'] ?? ''),
            'billing_month' => Sanitizer::sanitize($_POST['billing_month'] ?? $_GET['billing_month'] ?? ''),
            'amount_min' => $_POST['amount_min'] ?? $_GET['amount_min'] ?? '',
            'amount_max' => $_POST['amount_max'] ?? $_GET['amount_max'] ?? '',
        ];
        if ($selfOnly) {
            $filters['salesperson_user_id'] = (int)($_SESSION['user_id'] ?? 0);
            $filters['owner_didar_id'] = Sanitizer::sanitize($_SESSION['didar_id'] ?? '');
        } elseif (!$this->isLegacyCommissionManager()) {
            $filters = $this->applyCommissionScope($filters, $requestedOwnerDidarId);
        }
        try {
            $rows = $this->commissionService->getDealCommissions($filters);
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
            return;
        }
        $payload = [
            'commissions' => $rows,
            'read_model' => [
                'mode' => $this->commissionService->getCommissionReadModelMode(),
            ],
        ];
        if ($payload['read_model']['mode'] === 'dual_compare') {
            $payload['read_model']['comparison'] = $this->commissionService->getCommissionReadModelDiagnostics($filters);
        }
        $this->successResponse($payload);
    }

    public function calculateLedger()
    {
        $this->assertAdminOrCrm('commissions.ledger.calculate');
        $billingMonth = Sanitizer::sanitize($_POST['billing_month'] ?? $_GET['billing_month'] ?? DateNormalizer::currentJalaliMonth());
        try {
            $result = $this->commissionService->calculateLedgerForBillingMonth($billingMonth);
            $this->successResponse(['result' => $result], 'محاسبه پورسانت از روی دفتر پرداخت انجام شد');
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            Logger::logError('CALCULATE_COMMISSION_LEDGER_FAILED', $e, ['billing_month' => $billingMonth]);
            $this->errorResponse('خطا در محاسبه پورسانت از روی پرداخت‌ها', 500);
        }
    }

    public function previewLedger()
    {
        $this->assertAdminOrCrm('commissions.ledger.calculate');
        $defaultRange = DateNormalizer::currentJalaliMonthRange();
        $dateFrom = Sanitizer::sanitize($_POST['date_from'] ?? $_GET['date_from'] ?? $defaultRange['date_from']);
        $dateTo = Sanitizer::sanitize($_POST['date_to'] ?? $_GET['date_to'] ?? $defaultRange['date_to']);
        $limit = (int)($_POST['limit'] ?? $_GET['limit'] ?? 500);
        try {
            $preview = $this->commissionService->previewLedgerForDateRange($dateFrom, $dateTo, $limit);
            $this->successResponse(['preview' => $preview], 'پیش‌نمایش محاسبه پورسانت آماده شد');
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            Logger::logError('PREVIEW_COMMISSION_LEDGER_FAILED', $e, [
                'date_from' => $dateFrom,
                'date_to' => $dateTo,
            ]);
            $this->errorResponse('خطا در پیش‌نمایش محاسبه پورسانت', 500);
        }
    }

    public function getDealCommissionByDeal()
    {
        $selfOnly = (($_SESSION['role'] ?? '') === 'agent') && !$this->authService->isAdminOrCrmSpecialist();
        if (!$selfOnly) {
            $this->assertAdminOrCrm('commissions.view');
        }

        $dealDidarId = Sanitizer::sanitize($_POST['deal_didar_id'] ?? $_GET['deal_didar_id'] ?? '');
        if ($dealDidarId === '') {
            $this->errorResponse('شناسه معامله نامعتبر است', 422);
        }

        $row = $this->commissionService->getDealCommissionByDeal($dealDidarId);
        if ($row && $selfOnly) {
            $currentUserId = (int)($_SESSION['user_id'] ?? 0);
            $salespersonId = (int)($row['salesperson_user_id'] ?? $row['beneficiary_user_id'] ?? 0);
            if ($currentUserId <= 0 || $salespersonId !== $currentUserId) {
                $row = null;
            }
        }

        $this->successResponse(['commission' => $row]);
    }

    public function updateDealCommissionStatus()
    {
        $this->assertAdminOrCrm('commissions.status.manage');
        $id = (int)($_POST['commission_id'] ?? 0);
        $status = Sanitizer::sanitize($_POST['status'] ?? '');
        $actorUserId = (int)($_SESSION['user_id'] ?? 0);
        if ($id <= 0 || $status === '') {
            $this->errorResponse('ورودی نامعتبر است', 422);
        }
        $ok = $this->commissionService->updateDealCommissionStatus($id, $status, $actorUserId);
        if (!$ok) {
            $this->errorResponse('به‌روزرسانی وضعیت انجام نشد', 500);
        }
        $this->successResponse([], 'وضعیت پورسانت به‌روزرسانی شد');
    }

    public function deleteDealCommission()
    {
        $this->assertAdminOrCrm('commissions.delete');
        $id = (int)($_POST['commission_id'] ?? 0);
        if ($id <= 0) {
            $this->errorResponse('شناسه نامعتبر است', 422);
        }
        $role = trim((string)($_SESSION['role'] ?? ''));
        $actorUserId = (int)($_SESSION['user_id'] ?? 0);
        try {
            $this->commissionService->deleteDealCommission($id, $role, $actorUserId);
            $this->successResponse([], 'رکورد پورسانت حذف شد');
        } catch (\RuntimeException $e) {
            if ($e->getMessage() === 'FORBIDDEN_DELETE') {
                $this->errorResponse('شما مجاز به حذف این رکورد نیستید', 403);
            }
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            $this->errorResponse('خطا در حذف رکورد پورسانت', 500);
        }
    }

    public function bulkDeleteRules()
    {
        $this->assertAdminOrCrm('commissions.delete');
        $role = trim((string)($_SESSION['role'] ?? ''));
        $ids = $this->parseBulkDeleteIds($_POST['ids'] ?? ($_POST['rule_ids'] ?? '[]'));
        if (count($ids) < 1) {
            $this->errorResponse('حداقل یک رکورد معتبر برای حذف گروهی لازم است', 422);
        }

        try {
            $pdo = $this->commissionService->getRuleRepository()->getDb()->getPdo();
            $validIds = $this->validateBulkDeleteIdsExist($pdo, 'commission_rules', $ids);
            if (count($validIds) < 1) {
                $this->errorResponse('شناسه‌های ارسالی معتبر نیستند', 422);
            }
            $result = $this->commissionService->deleteCommissionRulesBulk($validIds, $role);
            if (empty($result['deleted'])) {
                $this->errorResponse('هیچ قانونی حذف نشد', 422);
            }
            $this->successResponse($result, 'حذف گروهی قوانین انجام شد');
        } catch (\RuntimeException $e) {
            if ($e->getMessage() === 'FORBIDDEN_DELETE') {
                $this->errorResponse('دسترسی حذف گروهی ندارید', 403);
            }
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            $this->errorResponse('خطا در حذف گروهی قوانین', 500);
        }
    }

    public function deleteRule()
    {
        $this->assertAdminOrCrm('commissions.delete');
        $id = (int)($_POST['rule_id'] ?? 0);
        if ($id <= 0) {
            $this->errorResponse('شناسه قانون نامعتبر است', 422);
        }
        $role = trim((string)($_SESSION['role'] ?? ''));
        try {
            $this->commissionService->deleteCommissionRule($id, $role);
            $this->successResponse([], 'قانون پورسانت حذف شد');
        } catch (\RuntimeException $e) {
            if ($e->getMessage() === 'FORBIDDEN_DELETE') {
                $this->errorResponse('شما مجاز به حذف این قانون نیستید', 403);
            }
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            $this->errorResponse('خطا در حذف قانون پورسانت', 500);
        }
    }

    public function bulkDeleteDealCommissions()
    {
        $this->assertAdminOrCrm('commissions.delete');
        $ids = $this->parseBulkDeleteIds($_POST['ids'] ?? '[]');
        if (count($ids) < 1) {
            $this->errorResponse('حداقل یک رکورد معتبر برای حذف گروهی لازم است', 422);
        }

        $role = trim((string)($_SESSION['role'] ?? ''));
        $actorUserId = (int)($_SESSION['user_id'] ?? 0);
        $pdo = $this->commissionService->getRuleRepository()->getDb()->getPdo();
        $validIds = $this->validateBulkDeleteIdsExist($pdo, 'deal_commissions', $ids);
        if (count($validIds) < 1) {
            $this->errorResponse('شناسه‌های ارسالی معتبر نیستند', 422);
        }
        $deleted = [];
        $skipped = [];
        foreach ($validIds as $id) {
            $cid = (int)$id;
            if ($cid <= 0) {
                continue;
            }
            try {
                $this->commissionService->deleteDealCommission($cid, $role, $actorUserId);
                $deleted[] = $cid;
            } catch (\RuntimeException $e) {
                $skipped[] = ['id' => $cid, 'reason' => $e->getMessage()];
            } catch (\Throwable $e) {
                $skipped[] = ['id' => $cid, 'reason' => 'error'];
            }
        }
        $this->successResponse(['deleted' => $deleted, 'skipped' => $skipped], 'حذف گروهی رکوردهای پورسانت انجام شد');
    }

    public function assignRules()
    {
        $this->assertAdminOrCrm('commissions.assignments.manage');
        try {
            $ruleIds = json_decode((string)($_POST['rule_ids'] ?? '[]'), true);
            $agentIds = json_decode((string)($_POST['agent_ids'] ?? '[]'), true);
            $productIds = json_decode((string)($_POST['product_ids'] ?? '[]'), true);
            $result = $this->commissionService->assignRules(
                is_array($ruleIds) ? $ruleIds : [],
                is_array($agentIds) ? $agentIds : [],
                is_array($productIds) ? $productIds : [],
                (int)($_SESSION['user_id'] ?? 0),
                (string)($_POST['source_type'] ?? 'INDIVIDUAL'),
                (int)($_POST['source_team_supervisor_user_id'] ?? 0)
            );
            $this->successResponse($result, 'تخصیص قوانین پورسانت انجام شد');
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            Logger::logError('ASSIGN_COMMISSION_RULES_FAILED', $e, ['post' => $_POST]);
            $this->errorResponse('خطا در تخصیص قوانین پورسانت', 500);
        }
    }

    public function toggleAssignment()
    {
        $this->assertAdminOrCrm('commissions.assignments.manage');
        $assignmentId = (int)($_POST['assignment_id'] ?? 0);
        $active = (int)($_POST['is_active'] ?? 0) === 1;
        if ($assignmentId <= 0) {
            $this->errorResponse('شناسه تخصیص نامعتبر است', 422);
        }
        $ok = $this->commissionService->setAssignmentActive($assignmentId, $active, (int)($_SESSION['user_id'] ?? 0));
        if (!$ok) {
            $this->errorResponse('تغییر وضعیت تخصیص انجام نشد', 500);
        }
        $this->successResponse([], 'وضعیت تخصیص به‌روزرسانی شد');
    }

    public function updateAssignment()
    {
        $this->assertAdminOrCrm('commissions.assignments.manage');
        $assignmentId = (int)($_POST['assignment_id'] ?? 0);
        $ruleId = (int)($_POST['rule_id'] ?? 0);
        $productIds = json_decode((string)($_POST['product_ids'] ?? '[]'), true);
        try {
            $ok = $this->commissionService->updateAssignmentRule(
                $assignmentId,
                $ruleId,
                is_array($productIds) ? $productIds : [],
                (int)($_SESSION['user_id'] ?? 0)
            );
            if (!$ok) {
                $this->errorResponse('تغییر قانون تخصیص انجام نشد', 500);
            }
            $this->successResponse([], 'قانون تخصیص به‌روزرسانی شد');
        } catch (\InvalidArgumentException $e) {
            $this->errorResponse($e->getMessage(), 422);
        } catch (\Throwable $e) {
            Logger::logError('UPDATE_COMMISSION_ASSIGNMENT_FAILED', $e, ['post' => $_POST]);
            $this->errorResponse('خطا در تغییر قانون تخصیص', 500);
        }
    }

    public function deleteAssignment()
    {
        $this->assertAdminOrCrm('commissions.assignments.manage');
        $assignmentId = (int)($_POST['assignment_id'] ?? 0);
        if ($assignmentId <= 0) {
            $this->errorResponse('شناسه تخصیص نامعتبر است', 422);
        }
        if (!$this->commissionService->deleteAssignment($assignmentId)) {
            $this->errorResponse('حذف تخصیص انجام نشد', 500);
        }
        $this->successResponse([], 'تخصیص حذف شد');
    }

    public function toggleRule()
    {
        $this->assertAdminOrCrm('commissions.rules.manage');
        $ruleId = (int)($_POST['rule_id'] ?? 0);
        $active = (int)($_POST['is_active'] ?? 0) === 1;
        if ($ruleId <= 0) {
            $this->errorResponse('شناسه قانون نامعتبر است', 422);
        }
        try {
            $this->commissionService->setRuleActive($ruleId, $active);
            $this->successResponse([], 'وضعیت قانون به‌روزرسانی شد');
        } catch (\Throwable $e) {
            $this->errorResponse('تغییر وضعیت قانون انجام نشد', 500);
        }
    }

    public function getSetupData()
    {
        $selfOnly = (($_SESSION['role'] ?? '') === 'agent') && !$this->authService->isAdminOrCrmSpecialist();
        if (!$selfOnly) {
            $this->assertAdminOrCrm('commissions.view');
        }
        $allUsers = $this->userRepo->getAll();
        if ($selfOnly) {
            $currentId = (int)($_SESSION['user_id'] ?? 0);
            $allUsers = array_values(array_filter($allUsers, static function ($u) use ($currentId) {
                return (int)($u->id ?? 0) === $currentId;
            }));
        }
        $users = array_map(static function ($user) {
            return [
                'id' => (int)$user->id,
                'display_name' => trim((string)($user->display_name ?: ($user->first_name . ' ' . $user->last_name))),
                'is_active' => (int)$user->is_active,
                'role' => (string)$user->role,
            ];
        }, $allUsers);

        $this->successResponse([
            'users' => $users,
        ]);
    }

    private function assertAdminOrCrm(string $capabilityKey = ''): void
    {
        if ($this->isLegacyCommissionManager()) {
            return;
        }
        if ($capabilityKey !== '' && $this->hasDelegatedCapability($capabilityKey)) {
            return;
        }
        $this->errorResponse('دسترسی محدود', 403);
    }

    private function isLegacyCommissionManager(): bool
    {
        return $this->authService->hasGlobalAdminOverride() || $this->authService->isCrmSpecialist();
    }

    private function applyCommissionScope(array $filters, string $requestedOwnerDidarId): array
    {
        global $app;
        if (isset($app->services->rbac) && $app->services->rbac) {
            return $app->services->rbac->applyOwnerScope($filters, 'commissions', $requestedOwnerDidarId);
        }
        $self = trim((string)($_SESSION['didar_id'] ?? ''));
        if ($self !== '') {
            $filters['owner_didar_id'] = $self;
        } else {
            $filters['owner_didar_id'] = '__rbac_scope_none__';
        }
        return $filters;
    }
}
