<?php

namespace App\Domains\Purchase\Controllers;

use App\Domains\Integration\Services\TelepagosIntegrationService;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Purchase\Models\PurchaseItem;
use App\Domains\Purchase\Requests\PaymentIntentRequest;
use App\Domains\Purchase\Requests\StartCheckoutRequest;
use App\Domains\Purchase\Requests\UpdatePurchaseCustomerRequest;
use App\Domains\Purchase\Requests\UpdatePurchaseItemQuantityRequest;
use App\Domains\Purchase\Resources\PurchaseResource;
use App\Domains\Purchase\Services\CheckoutService;
use App\Domains\Tenant\Models\Tenant;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
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
    {
        return PurchaseResource::collection(
            Purchase::query()
                ->where('tenant_codigo', $tenant->codigo)
                ->where('user_id', $request->user()->id)
                ->when($request->query('status'), function ($query, $status) {
                    $query->where('status', $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): PurchaseResource
    {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

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

        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 updateItemQuantity(
        UpdatePurchaseItemQuantityRequest $request,
        Tenant $tenant,
        Purchase $compra,
        PurchaseItem $item,
        CheckoutService $checkoutService,
    ): PurchaseResource {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

        return PurchaseResource::make(
            $checkoutService->updateItemQuantity(
                $compra,
                $item,
                (int) $request->validated('quantity'),
            ),
        );
    }

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

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

    public function paymentIntent(PaymentIntentRequest $request, Tenant $tenant, Purchase $compra): JsonResponse
    {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
        $method = $request->validated('method');
        $totalAmount = $compra->calculateCurrentTotalAmount();

        $purchaseUpdate = [
            'payment_method' => $method,
            'status' => Purchase::STATUS_PENDING_PAYMENT,
            'expires_at' => now()->addMinutes(
                max(1, (int) config("purchase.payment_expiration_minutes.{$method}", 30))
            ),
            'total' => $totalAmount,
        ];

        if ($method === 'transfer') {
            $purchaseUpdate['transfer_payer_dni'] = preg_replace('/\D+/', '', (string) $request->validated('transfer_payer_dni'));
        }

        $updated = Purchase::query()
            ->whereKey($compra->getKey())
            ->whereIn('status', [Purchase::STATUS_CREATED, Purchase::STATUS_PENDING_PAYMENT])
            ->where(function ($query): void {
                $query->whereNull('expires_at')
                    ->orWhere('expires_at', '>', now());
            })
            ->update($purchaseUpdate);

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

        $compra->refresh();

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

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

                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::error('Unable to retrieve TelePagos account information.', [
                    'purchase_id' => $compra->id,
                    'exception' => $e,
                ]);

                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::info("Generating QR for 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'] ?? '',
                ]);
            } catch (\Exception $e) {
                Log::error('Unable to generate TelePagos QR code.', [
                    'purchase_id' => $compra->id,
                    'exception' => $e,
                ]);

                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 complete(Request $request, Tenant $tenant, Purchase $compra, CheckoutService $checkoutService): PurchaseResource
    {
        $compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);

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

    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;
    }
}
