<?php

namespace App\Settlement\Controller;

use App\Auth\Entity\User;
use App\Settlement\Entity\Settlement;
use App\Settlement\Entity\WalletTransaction;
use App\Settlement\Repository\SettlementRepository;
use App\Settlement\Repository\WalletTransactionRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use OpenApi\Attributes as OA;
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 Symfony\Component\Uid\Uuid;

#[OA\Tag(name: 'Settlements')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class SettlementController extends BaseController
{
    public function __construct(
        private readonly SettlementRepository       $settlementRepo,
        private readonly WalletTransactionRepository $walletRepo,
        private readonly \App\Representation\Repository\RepresentationRepository $representationRepo,
        private readonly \App\Shared\Service\FileValidatorService $fileValidator,
        private readonly string $projectDir,
    ) {}

    // ── Wallet ────────────────────────────────────────────────────────────────

    #[OA\Get(
        path: '/api/v1/wallet/balance',
        summary: 'Get current wallet balance and recent transactions',
        security: [['bearerAuth' => []]],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Wallet balance and recent transactions',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', properties: [
                            new OA\Property(property: 'balance_rials', type: 'integer'),
                            new OA\Property(property: 'recent_transactions', type: 'array', items: new OA\Items(type: 'object')),
                        ], type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
        ]
    )]
    #[Route('/api/v1/wallet/balance', methods: ['GET'])]
    public function balance(#[CurrentUser] User $user): JsonResponse
    {
        $balance      = $this->settlementRepo->getWalletBalance($user);
        $transactions = array_map(
            fn(WalletTransaction $t) => $t->toArray(),
            $this->walletRepo->findByUser($user, 10)
        );

        return $this->success([
            'balance_rials'        => $balance,
            'recent_transactions'  => $transactions,
        ]);
    }

    #[OA\Get(
        path: '/api/v1/wallet/transactions',
        summary: 'List all wallet transactions for the authenticated user',
        security: [['bearerAuth' => []]],
        responses: [
            new OA\Response(
                response: 200,
                description: 'List of wallet transactions',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'array', items: new OA\Items(type: 'object')),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
        ]
    )]
    #[Route('/api/v1/wallet/transactions', methods: ['GET'])]
    public function transactions(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(100, max(1, (int) $request->query->get('limit', 50)));

        $transactions = array_map(
            fn(WalletTransaction $t) => $t->toArray(),
            $this->walletRepo->findByUser($user, $limit, ($page - 1) * $limit)
        );
        $total = $this->walletRepo->countByUser($user);

        // meta added inside the existing { data: { data: [...] } } envelope so
        // existing clients keep reading data.data unchanged.
        return $this->success([
            'data' => $transactions,
            'meta' => [
                'totalRecords' => $total,
                'totalPages'   => (int) ceil($total / $limit),
                'currentPage'  => $page,
            ],
        ]);
    }

    // ── Settlement Requests ───────────────────────────────────────────────────

    #[OA\Post(
        path: '/api/v1/settlement',
        summary: 'Request a new settlement (withdrawal)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['amount_rials'],
                properties: [
                    new OA\Property(property: 'amount_rials', type: 'integer', minimum: 1),
                    new OA\Property(property: 'bank_account', type: 'object', nullable: true),
                ]
            )
        ),
        responses: [
            new OA\Response(
                response: 201,
                description: 'Settlement request created',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 422, description: 'Validation error or insufficient balance'),
        ]
    )]
    #[Route('/api/v1/settlement', methods: ['POST'])]
    public function request(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data        = json_decode($request->getContent(), true) ?? [];
        $amountRials = (int) ($data['amount_rials'] ?? 0);
        $ibanId      = trim((string) ($data['iban_id'] ?? ''));

        if ($amountRials <= 0) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'مبلغ برداشت باید بیشتر از صفر باشد', 422);
        }

        if ($ibanId === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'انتخاب شماره شبا الزامی است', 422, 'iban_id');
        }

        $rep  = $this->representationRepo->findByUser($user);
        $iban = $rep?->findVerifiedIban($ibanId);
        if ($iban === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره شبا نامعتبر یا تأییدنشده است', 422, 'iban_id');
        }

        $balance = $this->settlementRepo->getWalletBalance($user);
        if ($amountRials > $balance) {
            return $this->error(ErrorCodes::ERR_CONFLICT_001, 'موجودی کافی نیست', 422);
        }

        // snapshot شبای انتخابی در خود رکورد ذخیره می‌شود تا حذف بعدی شبا، این رکورد را خراب نکند
        $bankAccount = [
            'iban'       => $iban['iban'],
            'bank_name'  => $iban['bank_name'] ?? null,
            'owner_name' => $iban['owner_name'] ?? null,
        ];

        $settlement = new Settlement($user, $amountRials, $bankAccount);
        $this->settlementRepo->save($settlement);

        // Reserve amount by debit transaction
        $tx = new WalletTransaction($user, $amountRials, WalletTransaction::TYPE_DEBIT, $balance - $amountRials);
        $tx->setDescription('درخواست برداشت ' . $settlement->getUuid());
        $this->walletRepo->save($tx);

        return $this->success(['data' => $settlement->toArray()], 201);
    }

    #[OA\Get(
        path: '/api/v1/settlement',
        summary: 'List settlement requests for the authenticated user',
        security: [['bearerAuth' => []]],
        responses: [
            new OA\Response(
                response: 200,
                description: 'List of settlement requests',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'array', items: new OA\Items(type: 'object')),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
        ]
    )]
    #[Route('/api/v1/settlement', methods: ['GET'])]
    public function listMine(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(100, max(1, (int) $request->query->get('limit', 50)));

        $settlements = array_map(
            fn(Settlement $s) => $s->toArray(),
            $this->settlementRepo->findByUser($user, $limit, ($page - 1) * $limit)
        );
        $total = $this->settlementRepo->countByUser($user);

        return $this->success([
            'data' => $settlements,
            'meta' => [
                'totalRecords' => $total,
                'totalPages'   => (int) ceil($total / $limit),
                'currentPage'  => $page,
            ],
        ]);
    }

    #[OA\Get(
        path: '/api/v1/settlement/{uuid}',
        summary: 'Get a settlement request by UUID (own or admin)',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Settlement request details',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[Route('/api/v1/settlement/{uuid}', methods: ['GET'])]
    public function get(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $settlement = $this->settlementRepo->findByUuid($uuid);
        if ($settlement === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }

        if ($settlement->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

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

    // ── Admin Actions ─────────────────────────────────────────────────────────

    #[OA\Post(
        path: '/api/v1/settlement/{uuid}/approve',
        summary: 'Approve a pending settlement request (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: false,
            content: new OA\JsonContent(
                properties: [
                    new OA\Property(property: 'note', type: 'string', nullable: true),
                ]
            )
        ),
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Settlement approved',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
            new OA\Response(response: 404, description: 'Not found'),
            new OA\Response(response: 422, description: 'Settlement is not in pending state'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/settlement/{uuid}/approve', methods: ['POST'])]
    public function approve(string $uuid, Request $request, #[CurrentUser] User $admin): JsonResponse
    {
        $settlement = $this->settlementRepo->findByUuid($uuid);
        if ($settlement === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }

        if ($settlement->getStatus() !== Settlement::STATUS_PENDING) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این درخواست قابل تأیید نیست', 422);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        $settlement->approve($admin->getId(), $data['note'] ?? null);
        $this->settlementRepo->save($settlement);

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

    #[OA\Post(
        path: '/api/v1/settlement/{uuid}/reject',
        summary: 'Reject a pending settlement request (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['note'],
                properties: [
                    new OA\Property(property: 'note', type: 'string', description: 'Rejection reason (required)'),
                ]
            )
        ),
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Settlement rejected and amount refunded to wallet',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
            new OA\Response(response: 404, description: 'Not found'),
            new OA\Response(response: 422, description: 'Settlement is not pending or note is missing'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/settlement/{uuid}/reject', methods: ['POST'])]
    public function reject(string $uuid, Request $request, #[CurrentUser] User $admin): JsonResponse
    {
        $settlement = $this->settlementRepo->findByUuid($uuid);
        if ($settlement === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }

        if ($settlement->getStatus() !== Settlement::STATUS_PENDING) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این درخواست قابل رد نیست', 422);
        }

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

        $settlement->reject($admin->getId(), $note);
        $this->settlementRepo->save($settlement);

        // Refund the reserved amount back to wallet
        $balance = $this->settlementRepo->getWalletBalance($settlement->getUser());
        $tx      = new WalletTransaction(
            $settlement->getUser(),
            $settlement->getAmountRials(),
            WalletTransaction::TYPE_CREDIT,
            $balance + $settlement->getAmountRials()
        );
        $tx->setDescription('برگشت برداشت رد شده ' . $settlement->getUuid());
        $this->walletRepo->save($tx);

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

    #[OA\Post(
        path: '/file/upload/clinic_pro/settlement/receipt',
        summary: 'آپلود رسید پرداخت تسویه (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        responses: [new OA\Response(response: 200, description: 'فایل آپلود شد')]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/file/upload/clinic_pro/settlement/receipt', methods: ['POST'])]
    public function uploadReceipt(Request $request): JsonResponse
    {
        return $this->handleFileUpload($request, 'settlements/receipts');
    }

    #[OA\Post(
        path: '/api/v1/settlement/{uuid}/paid',
        summary: 'ثبت پرداخت نهایی تسویه با رسید (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['receipt'],
                properties: [new OA\Property(property: 'receipt', type: 'string', description: 'URL رسید آپلودشده')]
            )
        ),
        parameters: [new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string'))],
        responses: [
            new OA\Response(response: 200, description: 'پرداخت ثبت شد'),
            new OA\Response(response: 404, description: 'یافت نشد'),
            new OA\Response(response: 422, description: 'وضعیت نامعتبر یا رسید خالی'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/settlement/{uuid}/paid', methods: ['POST'])]
    public function markPaid(string $uuid, Request $request): JsonResponse
    {
        $settlement = $this->settlementRepo->findByUuid($uuid);
        if ($settlement === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }

        // فقط درخواستِ تأییدشده قابل پرداخت است.
        if ($settlement->getStatus() !== Settlement::STATUS_APPROVED) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'فقط تسویه‌ی تأییدشده قابل پرداخت است', 422);
        }

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

        // مبلغ هنگام ثبتِ درخواست از کیف‌پول کسر (reserve) شده؛ اینجا فقط نهایی‌سازی می‌شود.
        $settlement->markPaid($receipt);
        $this->settlementRepo->save($settlement);

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

    #[OA\Post(
        path: '/api/v1/settlement/{uuid}/receipt',
        summary: 'ذخیره‌ی رسید پرداخت بدون نهایی‌سازی (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['receipt'],
                properties: [new OA\Property(property: 'receipt', type: 'string', description: 'URL رسید آپلودشده')]
            )
        ),
        parameters: [new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string'))],
        responses: [
            new OA\Response(response: 200, description: 'رسید ذخیره شد'),
            new OA\Response(response: 404, description: 'یافت نشد'),
            new OA\Response(response: 422, description: 'وضعیت نامعتبر یا رسید خالی'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/settlement/{uuid}/receipt', methods: ['POST'])]
    public function saveReceipt(string $uuid, Request $request): JsonResponse
    {
        $settlement = $this->settlementRepo->findByUuid($uuid);
        if ($settlement === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }

        // فقط روی تسویه‌ی تأییدشده می‌توان رسید گذاشت (قبل از تکمیل).
        if ($settlement->getStatus() !== Settlement::STATUS_APPROVED) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'فقط تسویه‌ی تأییدشده قابل ثبت رسید است', 422);
        }

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

        $settlement->setReceipt($receipt);
        $this->settlementRepo->save($settlement);

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

    private function handleFileUpload(Request $request, string $subDir): JsonResponse
    {
        $content     = $request->getContent();
        $disposition = $request->headers->get('Content-Disposition', '');
        preg_match('/filename=["\']?([^"\';\s]+)["\']?/i', $disposition, $m);
        $filename = $m[1] ?? 'receipt.jpg';

        $tmpPath = sys_get_temp_dir() . '/' . uniqid('upload_', true);
        file_put_contents($tmpPath, $content);

        try {
            $safeFilename = $this->fileValidator->sanitizeFilename($filename);
            $mime         = $this->fileValidator->detectMimeType($tmpPath);

            $year = date('Y'); $month = date('m');
            $dir  = $this->projectDir . '/public/uploads/' . $subDir . '/' . $year . '-' . $month;
            if (!is_dir($dir)) mkdir($dir, 0755, true);

            $storedName = uniqid('', true) . '_' . $safeFilename;
            rename($tmpPath, $dir . '/' . $storedName);

            $url = '/uploads/' . $subDir . '/' . $year . '-' . $month . '/' . $storedName;

            return $this->success([
                'uuid'     => Uuid::v4()->toRfc4122(),
                'url'      => $url,
                'filename' => $safeFilename,
                'filemime' => $mime,
            ]);
        } catch (\Throwable $e) {
            if (file_exists($tmpPath)) unlink($tmpPath);
            return $this->error(ErrorCodes::ERR_VALIDATION_001, $e->getMessage(), 422);
        }
    }
}
