<?php
namespace App\Controllers;

use App\Repositories\PaymentReviewRepository;
use App\Repositories\DealPaymentRepository;
use App\Repositories\FinanceApprovalRepository;
use App\Services\AuthService;
use App\Services\CommissionService;
use App\Database\Connection;
use App\Utils\Sanitizer;
use App\Utils\Logger;

class PaymentController extends ApiController
{
    private $paymentRepo;
    private $dealPaymentRepo;
    private $financeApprovalRepo;
    private $commissionService;
    private $authService;
    private $db;

    public function __construct(
        PaymentReviewRepository $paymentRepo,
        AuthService $authService,
        Connection $db,
        ?DealPaymentRepository $dealPaymentRepo = null,
        ?FinanceApprovalRepository $financeApprovalRepo = null,
        ?CommissionService $commissionService = null
    )
    {
        $this->paymentRepo = $paymentRepo;
        $this->authService = $authService;
        $this->db = $db;
        $this->dealPaymentRepo = $dealPaymentRepo;
        $this->financeApprovalRepo = $financeApprovalRepo;
        $this->commissionService = $commissionService;
    }

    private function canRbac(string $cap): bool
    {
        global $app;
        if (isset($app->services->rbac) && $app->services->rbac) {
            return (bool)$app->services->rbac->can($cap);
        }
        return false;
    }

    private function requireCap(string $cap, string $msg): void
    {
        if (!$this->canRbac($cap)) {
            $this->errorResponse($msg, 403);
        }
    }

    private function currentUserDidarId(): string
    {
        return (string)($_SESSION['didar_id'] ?? $_SESSION['user_id'] ?? '');
    }

    private function legacyDealPaymentsEnabled(): bool
    {
        $value = getenv('COMMISSION_LEGACY_DEAL_PAYMENTS_ENABLED');
        if ($value === false || $value === '') {
            return false;
        }
        return in_array(strtolower(trim((string)$value)), ['1', 'true', 'yes', 'on'], true);
    }

    private function rejectMergedLegacyFinanceRoute(): void
    {
        $this->errorResponse('این مسیر با مدیریت پرداخت و چک ادغام شده است.', 410);
    }

    public function list(): void
    {
        $this->requireCap('financial_payments.view', 'دسترسی محدود - شما مجاز به مشاهده پرداخت‌ها نیستید');
        $filters = [
            'sepidar_status' => Sanitizer::sanitize($_POST['sepidar_status'] ?? $_GET['sepidar_status'] ?? ''),
            'search' => Sanitizer::sanitize($_POST['search'] ?? $_GET['search'] ?? ''),
            'date_from' => Sanitizer::sanitize($_POST['date_from'] ?? $_GET['date_from'] ?? ''),
            'date_to' => Sanitizer::sanitize($_POST['date_to'] ?? $_GET['date_to'] ?? ''),
            'lead_owner_id' => Sanitizer::sanitize($_POST['lead_owner_id'] ?? $_GET['lead_owner_id'] ?? ''),
            'service' => Sanitizer::sanitize($_POST['service'] ?? $_GET['service'] ?? ''),
            'tafsili_status' => Sanitizer::sanitize($_POST['tafsili_status'] ?? $_GET['tafsili_status'] ?? ''),
            'image_status' => Sanitizer::sanitize($_POST['image_status'] ?? $_GET['image_status'] ?? ''),
            'limit' => (int)($_POST['limit'] ?? $_GET['limit'] ?? 50),
            'offset' => (int)($_POST['offset'] ?? $_GET['offset'] ?? 0),
        ];
        $filters['limit'] = max(1, min(200, (int)$filters['limit']));
        $filters['offset'] = max(0, (int)$filters['offset']);
        $rows = $this->paymentRepo->listPayments($filters);
        $total = $this->paymentRepo->countPayments($filters);
        $this->successResponse([
            'payments' => $rows,
            'count' => count($rows),
            'total' => $total,
            'limit' => $filters['limit'],
            'offset' => $filters['offset'],
            'page' => (int)floor($filters['offset'] / $filters['limit']) + 1,
        ]);
    }

    public function markSepidar(): void
    {
        $this->requireCap('financial_payments.sepidar', 'دسترسی محدود - شما مجاز به ثبت وضعیت سپیدار نیستید');
        $transactionId = (int)($_POST['transaction_id'] ?? 0);
        if ($transactionId <= 0) {
            $this->errorResponse('شناسه پرداخت نامعتبر است');
        }
        $txn = $this->paymentRepo->getPaymentTransaction($transactionId);
        if (!$txn) {
            $this->errorResponse('پرداخت یافت نشد');
        }
        $review = $this->paymentRepo->getReview($transactionId);
        if (!empty($review['sepidar_registered_at'])) {
            $this->successResponse(['review' => $review], 'این پرداخت قبلا در سپیدار ثبت شده است');
        }

        $review = $this->paymentRepo->markSepidarRegistered(
            $transactionId,
            (int)$txn['deal_id'],
            (float)$txn['amount'],
            $this->currentUserDidarId()
        );
        Logger::logInfo('PAYMENT_SEPIDAR_REGISTERED', ['transaction_id' => $transactionId, 'deal_id' => (int)$txn['deal_id']]);
        $this->successResponse(['review' => $review], 'ثبت سپیدار انجام شد');
    }

    public function overridePaymentOwner(): void
    {
        $this->requireCap('payments.owner_override', 'دسترسی محدود - شما مجاز به تغییر مالک پرداخت نیستید');
        $paymentRecordId = (int)($_POST['payment_record_id'] ?? 0);
        $ownerUserId = (int)($_POST['owner_user_id'] ?? 0);
        if ($paymentRecordId <= 0 || $ownerUserId <= 0) {
            $this->errorResponse('شناسه پرداخت و مالک جدید الزامی است', 422);
        }

        global $app;
        if (!isset($app->repositories->paymentRecord, $app->repositories->user, $app->repositories->paymentEvent)) {
            $this->errorResponse('سرویس مالک پرداخت پیکربندی نشده است', 500);
        }

        $oldRecord = $app->repositories->paymentRecord->findById($paymentRecordId);
        if (!$oldRecord) {
            $this->errorResponse('پرداخت یافت نشد', 404);
        }
        $owner = $app->repositories->user->findById($ownerUserId);
        if (!$owner || trim((string)($owner->didar_user_id ?? '')) === '') {
            $this->errorResponse('مالک جدید معتبر نیست', 422);
        }

        $ownerName = trim((string)($owner->display_name ?? ''));
        if ($ownerName === '') {
            $ownerName = trim((string)($owner->first_name ?? '') . ' ' . (string)($owner->last_name ?? ''));
        }
        $effectiveDate = trim((string)($oldRecord['effective_date'] ?? $oldRecord['payment_date'] ?? ''));
        $hierarchy = ['supervisor_user_id_snapshot' => null, 'manager_user_id_snapshot' => null, 'hierarchy_snapshot_at' => $effectiveDate ?: date('Y-m-d')];
        if (isset($app->services->userHierarchy) && $app->services->userHierarchy) {
            $hierarchy = $app->services->userHierarchy->resolveSnapshot($ownerUserId, $hierarchy['hierarchy_snapshot_at']);
        }

        $snapshot = array_merge([
            'payment_owner_user_id' => $ownerUserId,
            'payment_owner_didar_id' => (string)$owner->didar_user_id,
            'payment_owner_name_snapshot' => $ownerName !== '' ? $ownerName : (string)$owner->didar_user_id,
        ], $hierarchy);

        if (!$app->repositories->paymentRecord->overrideOwner($paymentRecordId, $snapshot)) {
            $this->errorResponse('تغییر مالک پرداخت انجام نشد', 500);
        }

        $this->syncPaymentOwnerToLinkedTransactions($paymentRecordId, $snapshot);
        $newRecord = $app->repositories->paymentRecord->findById($paymentRecordId) ?: $snapshot;
        $app->repositories->paymentEvent->recordEvent(
            $paymentRecordId,
            'PAYMENT_OWNER_OVERRIDDEN',
            $oldRecord,
            $newRecord,
            ['reason' => trim((string)Sanitizer::sanitize($_POST['reason'] ?? ''))],
            (int)($_SESSION['user_id'] ?? 0),
            (string)($_SESSION['didar_id'] ?? '')
        );

        Logger::logInfo('PAYMENT_OWNER_OVERRIDDEN', [
            'payment_record_id' => $paymentRecordId,
            'new_owner_user_id' => $ownerUserId,
            'actor_user_id' => (int)($_SESSION['user_id'] ?? 0),
        ]);
        $this->successResponse(['payment_record' => $newRecord], 'مالک پرداخت تغییر کرد');
    }

    private function syncPaymentOwnerToLinkedTransactions(int $paymentRecordId, array $snapshot): void
    {
        if ($paymentRecordId <= 0 || !$this->db->hasColumn('transactions', 'payment_owner_user_id')) {
            return;
        }
        try {
            $pdo = $this->db->getPdo();
            $ids = [];
            if ($this->db->hasColumn('transactions', 'payment_record_id')) {
                $stmt = $pdo->prepare('SELECT id FROM transactions WHERE payment_record_id = ?');
                $stmt->execute([$paymentRecordId]);
                $ids = array_merge($ids, array_map('intval', $stmt->fetchAll(\PDO::FETCH_COLUMN) ?: []));
            }
            $stmt = $pdo->prepare("SELECT linked_id FROM payment_record_links WHERE payment_record_id = ? AND linked_table = 'transactions'");
            $stmt->execute([$paymentRecordId]);
            $ids = array_merge($ids, array_map('intval', $stmt->fetchAll(\PDO::FETCH_COLUMN) ?: []));
            $ids = array_values(array_unique(array_filter($ids)));
            if (empty($ids)) {
                return;
            }
            $set = ['payment_owner_user_id = ?', 'payment_owner_didar_id = ?', 'payment_owner_name_snapshot = ?'];
            $params = [
                (int)$snapshot['payment_owner_user_id'],
                (string)$snapshot['payment_owner_didar_id'],
                (string)$snapshot['payment_owner_name_snapshot'],
            ];
            if ($this->db->hasColumn('transactions', 'payment_owner_locked_at')) {
                $set[] = 'payment_owner_locked_at = NOW()';
            }
            $params = array_merge($params, $ids);
            $sql = 'UPDATE transactions SET ' . implode(', ', $set) . ' WHERE id IN (' . implode(',', array_fill(0, count($ids), '?')) . ')';
            $pdo->prepare($sql)->execute($params);
        } catch (\Throwable $e) {
            Logger::logWarning('Failed to sync overridden payment owner to linked transactions', [
                'payment_record_id' => $paymentRecordId,
                'error' => $e->getMessage(),
            ]);
        }
    }

    public function financeChequeInbox(): void
    {
        $this->requireCap('financial_payments.view', 'دسترسی محدود - شما مجاز به مشاهده کارتابل مالی پورسانت نیستید');
        if (!$this->legacyDealPaymentsEnabled()) {
            $this->rejectMergedLegacyFinanceRoute();
        }
        if (!$this->financeApprovalRepo) {
            $this->errorResponse('مخزن تایید مالی پیکربندی نشده است', 500);
        }
        $fromDate = Sanitizer::sanitize($_POST['date_from'] ?? $_GET['date_from'] ?? '');
        $toDate = Sanitizer::sanitize($_POST['date_to'] ?? $_GET['date_to'] ?? '');
        $rows = $this->financeApprovalRepo->getChequeFinanceInbox($fromDate, $toDate);
        $this->successResponse(['rows' => $rows, 'count' => count($rows)]);
    }

    public function approveFinanceCheque(): void
    {
        $this->requireCap('financial_payments.sepidar', 'دسترسی محدود - شما مجاز به تایید مالی چک نیستید');
        if (!$this->legacyDealPaymentsEnabled()) {
            $this->rejectMergedLegacyFinanceRoute();
        }
        $paymentId = (int)($_POST['deal_payment_id'] ?? 0);
        $cashedDate = trim((string)Sanitizer::sanitize($_POST['cashed_date'] ?? ''));
        if ($paymentId <= 0 || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $cashedDate)) {
            $this->errorResponse('ورودی تایید چک نامعتبر است', 422);
        }
        if (!$this->dealPaymentRepo || !$this->financeApprovalRepo) {
            $this->errorResponse('سرویس تایید مالی پیکربندی نشده است', 500);
        }
        $payment = $this->dealPaymentRepo->findById($paymentId);
        if (!$payment || strtoupper((string)$payment->payment_type) !== 'CHEQUE') {
            $this->errorResponse('چک یافت نشد', 404);
        }

        $actorUserId = (int)($_SESSION['user_id'] ?? 0);
        $this->dealPaymentRepo->markChequeCashed($paymentId, $cashedDate, $actorUserId);
        $this->financeApprovalRepo->approveCheque($paymentId, (string)$payment->deal_didar_id, (float)$payment->payment_amount, $actorUserId);
        if ($this->commissionService) {
            $this->commissionService->calculateAndStoreForWonDeal((string)$payment->deal_didar_id);
        }
        Logger::logInfo('FINANCE_CHEQUE_APPROVED', ['deal_payment_id' => $paymentId, 'deal_didar_id' => (string)$payment->deal_didar_id]);
        $this->successResponse([], 'چک تایید و وصول شد');
    }

    public function rejectFinanceCheque(): void
    {
        $this->requireCap('financial_payments.sepidar', 'دسترسی محدود - شما مجاز به رد مالی چک نیستید');
        if (!$this->legacyDealPaymentsEnabled()) {
            $this->rejectMergedLegacyFinanceRoute();
        }
        $paymentId = (int)($_POST['deal_payment_id'] ?? 0);
        $reason = trim((string)Sanitizer::sanitize($_POST['reason'] ?? ''));
        if ($paymentId <= 0 || $reason === '') {
            $this->errorResponse('شناسه چک و دلیل رد الزامی است', 422);
        }
        if (!$this->dealPaymentRepo || !$this->financeApprovalRepo) {
            $this->errorResponse('سرویس تایید مالی پیکربندی نشده است', 500);
        }
        $payment = $this->dealPaymentRepo->findById($paymentId);
        if (!$payment || strtoupper((string)$payment->payment_type) !== 'CHEQUE') {
            $this->errorResponse('چک یافت نشد', 404);
        }

        $actorUserId = (int)($_SESSION['user_id'] ?? 0);
        $this->dealPaymentRepo->markChequeRejected($paymentId, $reason, $actorUserId);
        $this->financeApprovalRepo->rejectCheque($paymentId, (string)$payment->deal_didar_id, $reason, $actorUserId);
        Logger::logInfo('FINANCE_CHEQUE_REJECTED', ['deal_payment_id' => $paymentId, 'deal_didar_id' => (string)$payment->deal_didar_id]);
        $this->successResponse([], 'رد چک ثبت شد');
    }
}
