<?php

namespace App\Domains\Purchase\Controllers;

use App\Domains\Integration\Services\TelepagosIntegrationService;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Purchase\Requests\PaymentIntentRequest;
use App\Domains\Purchase\Requests\StartCheckoutRequest;
use App\Domains\Purchase\Requests\UpdatePurchaseCustomerRequest;
use App\Domains\Purchase\Resources\PurchaseResource;
use App\Domains\Purchase\Services\Checkout\PurchaseResponseLoader;
use App\Domains\Purchase\Services\CheckoutService;
use App\Domains\Purchase\Services\PurchaseStateGuard;
use App\Domains\Tenant\Models\Tenant;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class PurchaseController extends Controller
{
    public function index(Request $request, Tenant $tenant): JsonResponse
    {
        $statusParam = $request->query('status');
        $statuses = is_string($statusParam)
            ? collect(explode(',', $statusParam))
                ->map(fn (string $status): string => trim($status))
                ->filter()
                ->unique()
                ->values()
                ->all()
            : [];

        return PurchaseResource::collection(
            Purchase::query()
                ->with('stockReservation')
                ->where('tenant_codigo', $tenant->codigo)
                ->where('user_id', $request->user()->id)
                ->when($statuses !== [], fn ($query) => $query->whereIn('status', $statuses))
                ->orderBy('status')
                ->latest()
                ->paginateFromRequest()
        )->response();
    }

    public function startCheckout(
        StartCheckoutRequest $request,
        Tenant $tenant,
        CheckoutService $checkoutService,
    ): JsonResponse {
        $data = $request->validated();

        $purchase = $checkoutService->startCheckout(
            $tenant,
            $request->user()->id,
            $data
        );

        return PurchaseResource::make($purchase)->response()->setStatusCode(201);
    }

    public function show(
        Request $request,
        Tenant $tenant,
        Purchase $compra,
        PurchaseResponseLoader $responses,
    ): PurchaseResource {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

        $compra->loadMissing('items')->loadCount('tickets');
        $responses->load($compra);

        return PurchaseResource::make($compra);
    }

    public function updateCustomerData(
        UpdatePurchaseCustomerRequest $request,
        Tenant $tenant,
        Purchase $compra,
        CheckoutService $checkoutService,
    ): PurchaseResource {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

        return PurchaseResource::make(
            $checkoutService->updateCustomerData($compra, $request->validated()),
        );
    }

    public function paymentIntent(
        PaymentIntentRequest $request,
        Tenant $tenant,
        Purchase $compra,
        PurchaseStateGuard $purchaseState,
    ): JsonResponse {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
        $method = $request->validated('method');
        $transferPayerDni = $method === 'transfer'
            ? preg_replace('/\D+/', '', (string) $request->validated('transfer_payer_dni'))
            : null;

        $updated = DB::transaction(function () use (
            $compra,
            $method,
            $purchaseState,
            $transferPayerDni,
        ): bool {
            /** @var Purchase|null $purchase */
            $purchase = Purchase::query()
                ->whereKey($compra->getKey())
                ->lockForUpdate()
                ->first();

            if ($purchase === null) {
                return false;
            }

            $purchaseState->assertNotExpired($purchase);

            if (
                ! in_array($purchase->status, [
                    Purchase::STATUS_CREATED,
                    Purchase::STATUS_PENDING_PAYMENT,
                ], true)
            ) {
                return false;
            }

            $purchaseState->lockCurrentCart($purchase);

            $purchaseUpdate = [
                'payment_method' => $method,
                'status' => Purchase::STATUS_PENDING_PAYMENT,
                'total' => $purchase->calculateCurrentTotalAmount(),
            ];

            if ($transferPayerDni !== null) {
                $purchaseUpdate['transfer_payer_dni'] = $transferPayerDni;
            }
            $purchase->update($purchaseUpdate);

            return true;
        });

        if (! $updated) {
            throw ValidationException::withMessages([
                'purchase' => __('api.purchase.not_available_for_payment'),
            ]);
        }

        $compra->refresh();
        $totalAmount = (float) $compra->total;

        if ($method === 'transfer') {
            $telepagosService = new TelepagosIntegrationService;
            $telepagosService->forTenant($tenant->codigo);

            try {
                $accountInfo = $telepagosService->getAccountInfo();

                Log::channel('telepagos')->info('Telepagos account information retrieved.', [
                    'tenant_code' => $tenant->codigo,
                    'purchase_id' => $compra->id,
                ]);

                return response()->json([
                    'payment_method' => 'transfer',
                    'transfer_data' => [
                        'titular' => $accountInfo['holder'] ?? null,
                        'cvu' => $accountInfo['cvu'] ?? null,
                        'alias' => $accountInfo['alias'] ?? null,
                        'cuit' => $accountInfo['cuit'] ?? null,
                        'entidad' => $accountInfo['entity'] ?? 'Telepagos S.A.',
                    ],
                ]);
            } catch (\Exception $e) {
                Log::channel('telepagos')->error('Unable to retrieve TelePagos account information.', [
                    'tenant_code' => $tenant->codigo,
                    'purchase_id' => $compra->id,
                    'error' => $e->getMessage(),
                ]);

                return response()->json([
                    'code' => 'integration.account_info_failed',
                    'message' => __('api.integration.account_info_failed'),
                ], 500);
            }
        }

        if ($method === 'qr') {
            $telepagosService = new TelepagosIntegrationService;
            $telepagosService->forTenant($tenant->codigo);

            try {
                Log::channel('telepagos')->info('Generating Telepagos QR.', [
                    'tenant_code' => $tenant->codigo,
                    'purchase_id' => $compra->id,
                    'amount' => $totalAmount,
                ]);
                $qrResponse = $telepagosService->generateQr(
                    $totalAmount,
                    'Compra',
                    "Compra #{$compra->id}"
                );

                $telepagosQr = $compra->telepagosQr()->create([
                    'qr_order_id' => (string) ($qrResponse['qr_order_id'] ?? ''),
                    'qr_code' => $qrResponse['qr_code'] ?? '',
                ]);

                Log::channel('telepagos')->info('Telepagos QR generated successfully.', [
                    'tenant_code' => $tenant->codigo,
                    'purchase_id' => $compra->id,
                    'qr_order_id' => $telepagosQr->qr_order_id,
                ]);
            } catch (\Exception $e) {
                Log::channel('telepagos')->error('Unable to generate TelePagos QR code.', [
                    'tenant_code' => $tenant->codigo,
                    'purchase_id' => $compra->id,
                    'error' => $e->getMessage(),
                ]);

                return response()->json([
                    'code' => 'integration.qr_generation_failed',
                    'message' => __('api.integration.qr_generation_failed'),
                ], 500);
            }

            return response()->json([
                'payment_method' => 'qr',
                'qr_data' => [
                    'qr_code' => $telepagosQr->qr_code,
                    'qr_order_id' => $telepagosQr->qr_order_id,
                ],
            ]);
        }

        return response()->json([
            'code' => 'integration.invalid_payment_method',
            'message' => __('api.integration.invalid_payment_method'),
        ], 400);
    }

    public function submitForReview(
        Request $request,
        Tenant $tenant,
        Purchase $compra,
        CheckoutService $checkoutService,
    ): PurchaseResource {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

        return PurchaseResource::make(
            $checkoutService->submitForReview($compra),
        );
    }

    public function cancel(Request $request, Tenant $tenant, Purchase $compra, CheckoutService $checkoutService): PurchaseResource
    {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

        return PurchaseResource::make(
            $checkoutService->cancelPurchase($compra)
        );
    }

    protected function resolveScopedPurchase(Tenant $tenant, int $userId, Purchase $purchase): Purchase
    {
        if ($purchase->tenant_codigo !== $tenant->codigo || $purchase->user_id !== $userId) {
            throw new NotFoundHttpException(__('api.errors.not_found'));
        }

        return $purchase;
    }
}
