<?php

namespace App\Sms\Controller;

use App\Auth\Entity\User;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Payment\Entity\Payment;
use App\Payment\Gateway\GatewayFactory;
use App\Payment\Repository\PaymentRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Sms\Entity\SmsSettings;
use App\Sms\Repository\SmsSettingsRepository;
use App\Sms\Repository\SmsWalletRepository;
use App\Sms\Repository\SmsWalletTransactionRepository;
use App\Sms\Service\SmsWalletService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'SMS')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class SmsWalletController extends BaseController
{
    private const SMS_PRICE_RIALS = 500;

    public function __construct(
        private readonly SmsWalletService               $walletService,
        private readonly SmsWalletRepository            $walletRepo,
        private readonly SmsWalletTransactionRepository $txRepo,
        private readonly SmsSettingsRepository          $settingsRepo,
        private readonly PaymentRepository              $paymentRepo,
        private readonly GatewayFactory                 $gateways,
        private readonly DoctorRepository               $doctorRepo,
        private readonly ClinicRepository               $clinicRepo,
        private readonly \App\Config\Repository\SiteConfigRepository $configRepo,
        private readonly string $appBaseUrl,
    ) {}

    /** قیمت هر پیامک از تنظیمات سایت؛ با fallback به مقدار پیش‌فرض. */
    private function smsPriceRials(): int
    {
        return max(1, (int) ($this->configRepo->get('sms_price_rials') ?: self::SMS_PRICE_RIALS));
    }

    #[Route('/api/v1/sms/wallet/balance', methods: ['GET'])]
    public function balance(#[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $balanceRials  = $this->walletService->getBalance($entityType, $entityId);
        $smsPriceRials = $this->smsPriceRials();
        $estimatedSms  = (int) floor($balanceRials / $smsPriceRials);

        return $this->success([
            'balance_rials'         => $balanceRials,
            'sms_price_rials'       => $smsPriceRials,
            'estimated_sms_count'   => $estimatedSms,
        ]);
    }

    #[Route('/api/v1/sms/wallet/charge', methods: ['POST'])]
    public function charge(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $data        = json_decode($request->getContent(), true) ?? [];
        $gatewayName = trim($data['gateway'] ?? 'mellat');
        $amountRials = (int) ($data['amount_rials'] ?? 0);

        if ($amountRials <= 0) {
            return $this->error(ErrorCodes::ERR_PAYMENT_002, 'مبلغ شارژ نامعتبر است', 422);
        }

        // فقط اعتبارسنجی درگاه؛ ارتباط با بانک در flow واحد (GET /payment/pay) انجام می‌شود.
        if ($this->gateways->resolve($gatewayName) === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'درگاه پرداخت نامعتبر است', 422);
        }

        $frontendAddress = trim($data['frontend_address'] ?? '');
        $payment = new Payment($user, $amountRials, $gatewayName, Payment::TYPE_SMS_WALLET, $frontendAddress);
        $payment->setMetadata(['entity_type' => $entityType, 'entity_id' => $entityId]);
        $this->paymentRepo->save($payment);

        return $this->success([
            'payment_uuid' => $payment->getUuid(),
            'pay_url'      => $this->appBaseUrl . '/api/v1/payment/pay/' . $payment->getOrderId(),
            'order_id'     => $payment->getOrderId(),
        ]);
    }

    #[Route('/api/v1/sms/wallet/logs', methods: ['GET'])]
    public function logs(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $page   = max(1, (int) $request->query->get('page', 1));
        $limit  = min(50, max(10, (int) $request->query->get('limit', 20)));
        $wallet = $this->walletService->getOrCreate($entityType, $entityId);

        $txs   = $this->txRepo->findByWallet($wallet, $page, $limit);
        $total = $this->txRepo->countByWallet($wallet);

        return $this->paginated(
            array_map(fn($tx) => $tx->toArray(), $txs),
            $total,
            $page,
            $limit
        );
    }

    #[Route('/api/v1/sms/settings', methods: ['GET'])]
    public function getSettings(#[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $settings = $this->settingsRepo->findByEntity($entityType, $entityId);

        if ($settings === null) {
            return $this->success([
                'entity_type'           => $entityType,
                'entity_id'             => $entityId,
                'reminder_enabled'      => false,
                'reminder_hours_before' => 2,
                'post_visit_enabled'    => false,
                'post_visit_text'       => null,
            ]);
        }

        return $this->success($settings->toArray());
    }

    #[Route('/api/v1/sms/settings', methods: ['PATCH'])]
    public function updateSettings(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $settings = $this->settingsRepo->findByEntity($entityType, $entityId);
        if ($settings === null) {
            $settings = new SmsSettings($entityType, $entityId);
        }

        $data = json_decode($request->getContent(), true) ?? [];

        if (isset($data['reminder_enabled']))      { $settings->setReminderEnabled((bool) $data['reminder_enabled']); }
        if (isset($data['reminder_hours_before'])) { $settings->setReminderHoursBefore((int) $data['reminder_hours_before']); }
        if (isset($data['post_visit_enabled']))     { $settings->setPostVisitEnabled((bool) $data['post_visit_enabled']); }
        if (array_key_exists('post_visit_text', $data) && $data['post_visit_text'] !== null) {
            $text = trim((string) $data['post_visit_text']);
            if ($text !== '') {
                $settings->submitPostVisitText($text);
            }
        }

        $this->settingsRepo->save($settings);

        return $this->success($settings->toArray());
    }

    #[Route('/api/v1/admin/sms/settings/review', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminReviewList(Request $request): JsonResponse
    {
        $status = $request->query->get('status', SmsSettings::TEXT_STATUS_PENDING);
        if (!in_array($status, [SmsSettings::TEXT_STATUS_PENDING, SmsSettings::TEXT_STATUS_APPROVED], true)) {
            $status = SmsSettings::TEXT_STATUS_PENDING;
        }

        $pending = $this->settingsRepo->createQueryBuilder('s')
            ->where('s.postVisitTextStatus = :status')
            ->setParameter('status', $status)
            ->getQuery()
            ->getResult();

        $data = array_map(function (SmsSettings $s) {
            $row = $s->toArray();
            $row['entity_name'] = $this->resolveEntityName($s->getEntityType(), $s->getEntityId());
            return $row;
        }, $pending);

        return $this->success(['data' => $data]);
    }

    #[Route('/api/v1/admin/sms/settings/{id}/approve', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminApprove(int $id): JsonResponse
    {
        $settings = $this->settingsRepo->find($id);
        if ($settings === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'تنظیمات یافت نشد', 404);
        }

        $settings->approvePostVisitText();
        $this->settingsRepo->save($settings);

        return $this->success($settings->toArray());
    }

    #[Route('/api/v1/admin/sms/settings/{id}/reject', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminReject(int $id, Request $request): JsonResponse
    {
        $settings = $this->settingsRepo->find($id);
        if ($settings === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'تنظیمات یافت نشد', 404);
        }

        $data   = json_decode($request->getContent(), true) ?? [];
        $reason = trim($data['reason'] ?? '');
        if ($reason === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'دلیل رد الزامی است', 422);
        }

        $settings->rejectPostVisitText($reason);
        $this->settingsRepo->save($settings);

        return $this->success($settings->toArray());
    }

    private function resolveEntityName(string $type, int $id): ?string
    {
        if ($type === 'doctor') {
            return $this->doctorRepo->find($id)?->getName();
        }
        if ($type === 'clinic') {
            return $this->clinicRepo->find($id)?->getName();
        }
        return null;
    }

    #[Route('/api/v1/admin/sms/wallet-report', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminReport(Request $request): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(100, max(10, (int) $request->query->get('limit', 20)));

        $total = (int) $this->walletRepo->createQueryBuilder('w')
            ->select('COUNT(w.id)')
            ->getQuery()
            ->getSingleScalarResult();

        $wallets = $this->walletRepo->createQueryBuilder('w')
            ->orderBy('w.balanceRials', 'DESC')
            ->setFirstResult(($page - 1) * $limit)
            ->setMaxResults($limit)
            ->getQuery()
            ->getArrayResult();

        return $this->paginated($wallets, $total, $page, $limit);
    }

    private function resolveEntity(User $user): array
    {
        if ($user->hasRole('ROLE_DOCTOR')) {
            $doctor = $this->doctorRepo->findByUser($user);
            return $doctor !== null ? ['doctor', $doctor->getId()] : ['doctor', null];
        }

        if ($user->hasRole('ROLE_CLINIC')) {
            $clinic = $this->clinicRepo->findByUser($user);
            return $clinic !== null ? ['clinic', $clinic->getId()] : ['clinic', null];
        }

        return ['unknown', null];
    }
}
