<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\Api\PaymentInitRequest;
use App\Http\Resources\Api\OrderResource;
use App\Models\Emplette;
use App\Models\Order;
use App\Services\AdminAlertMailService;
use App\Services\FedapayService;
use App\Services\NotificationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class PaymentController extends Controller
{
    public function __construct(
        private readonly NotificationService $notifications,
        private readonly FedapayService $fedapay,
        private readonly AdminAlertMailService $adminMails
    )
    {
    }

    public function init(PaymentInitRequest $request): JsonResponse
    {
        $orderId = (int) $request->validated('order_id');
        $order = Order::where('user_id', $request->user()->id)->findOrFail($orderId);

        if ($order->payment_status === Order::PAYMENT_PAID) {
            return response()->json(['message' => __('Order already paid.')], 422);
        }

        // Fallback dev/local quand Fedapay n'est pas activé.
        if (! $this->fedapay->isEnabled()) {
            $redirectUrl = url('/api/v1/payment/simulate?order_id=' . $order->id . '&token=' . $order->id);

            return response()->json([
                'data' => [
                    'order_id' => $order->id,
                    'redirect_url' => $redirectUrl,
                ],
                'message' => __('Redirect to payment (simulation).'),
            ]);
        }

        try {
            $payment = $this->fedapay->createTransaction($order, $request->user());
        } catch (\RuntimeException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $order->update([
            'payment_status' => Order::PAYMENT_PENDING,
            'payment_reference' => 'fedapay_tx_' . $payment['transaction_id'],
        ]);

        return response()->json([
            'data' => [
                'order_id' => $order->id,
                'redirect_url' => $payment['redirect_url'],
            ],
            'message' => __('Redirect to payment.'),
        ]);
    }

    public function verify(Request $request, Order $order): JsonResponse
    {
        if ($order->user_id !== $request->user()->id) {
            abort(404);
        }

        if (! str_starts_with((string) $order->payment_reference, 'fedapay_tx_') || ! $this->fedapay->isEnabled()) {
            return response()->json([
                'data' => [
                    'order' => new OrderResource($order->load('items', 'address')),
                    'payment_gateway_status' => null,
                ],
                'message' => __('Payment status synced.'),
            ]);
        }

        $transactionId = (string) str_replace('fedapay_tx_', '', (string) $order->payment_reference);

        try {
            $gatewayStatus = $this->fedapay->fetchTransactionStatus($transactionId);
        } catch (\RuntimeException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $wasDeleted = $this->applyGatewayStatus($order, $gatewayStatus);

        if ($wasDeleted) {
            return response()->json([
                'data' => [
                    'order_deleted' => true,
                    'payment_gateway_status' => $gatewayStatus['raw_status'],
                ],
                'message' => __('Paiement échoué: commande supprimée.'),
            ]);
        }

        return response()->json([
            'data' => [
                'order_deleted' => false,
                'order' => new OrderResource($order->fresh()->load('items', 'address')),
                'payment_gateway_status' => $gatewayStatus['raw_status'],
            ],
            'message' => __('Payment status synced.'),
        ]);
    }

    /**
     * Page de retour navigateur après paiement (GET).
     * À utiliser comme callback_url FedaPay — pas l’URL du webhook.
     */
    public function returnPage(Request $request): Response
    {
        $orderId = (string) $request->query('order_id', '');
        $safeId = e($orderId);

        $html = '<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Paiement — Caddies+</title></head><body style="font-family:system-ui,-apple-system,sans-serif;padding:28px;max-width:520px;margin:0 auto;line-height:1.5;color:#0f172a;">';
        $html .= '<h1 style="font-size:1.35rem;font-weight:700;margin:0 0 12px;">Paiement</h1>';
        $html .= '<p style="margin:0 0 16px;">Vous pouvez <strong>fermer cette page</strong> et retourner dans <strong>l’application Caddies+</strong> pour voir le statut de votre commande.</p>';
        if ($orderId !== '') {
            $html .= '<p style="margin:0;color:#64748b;font-size:0.9rem;">Réf. commande : ' . $safeId . '</p>';
        }
        $html .= '</body></html>';

        return response($html, 200)->header('Content-Type', 'text/html; charset=UTF-8');
    }

    /**
     * Si le navigateur ouvre l’URL du webhook en GET (mauvaise config callback),
     * on évite l’erreur 405 et on oriente vers la bonne URL ou une explication.
     */
    public function webhookGet(Request $request): Response|RedirectResponse
    {
        $orderId = $request->query('order_id');
        if ($orderId !== null && $orderId !== '') {
            return redirect()->away(
                url('/api/v1/payment/return?' . http_build_query(['order_id' => $orderId]))
            );
        }

        $html = '<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Webhook</title></head><body style="font-family:system-ui,sans-serif;padding:28px;max-width:560px;margin:0 auto;line-height:1.55;color:#0f172a;">';
        $html .= '<p><strong>Cette adresse est réservée aux notifications FedaPay</strong> (requêtes <code>POST</code> signées), pas au retour utilisateur dans le navigateur.</p>';
        $html .= '<p>Dans le tableau de bord FedaPay, configurez :</p><ul><li><strong>Webhook</strong> → <code>' . e(url('/api/v1/payment/webhook')) . '</code> (POST)</li>';
        $html .= '<li><strong>Retour / callback</strong> → <code>' . e(url('/api/v1/payment/return')) . '?order_id=…</code> ou laissez vide pour utiliser la valeur par défaut de l’API.</li></ul>';
        $html .= '</body></html>';

        return response($html, 200)->header('Content-Type', 'text/html; charset=UTF-8');
    }

    public function webhook(Request $request): JsonResponse
    {
        $signature = (string) ($request->header('x-fedapay-signature') ?? $request->header('X-FEDAPAY-SIGNATURE'));
        if (! $this->fedapay->isValidWebhookSignature((string) $request->getContent(), $signature)) {
            return response()->json(['message' => 'Invalid webhook signature'], 401);
        }

        $payload = $request->all();
        $orderId = data_get($payload, 'data.transaction.custom_data.order_id')
            ?? data_get($payload, 'data.transaction.custom_metadata.order_id')
            ?? data_get($payload, 'data.transaction.metadata.order_id')
            ?? data_get($payload, 'order_id');
        $transactionId = data_get($payload, 'data.transaction.id')
            ?? data_get($payload, 'transaction.id')
            ?? data_get($payload, 'id');

        if (! $orderId) {
            if (! $transactionId) {
                return response()->json(['message' => 'Missing order_id'], 400);
            }
            $order = Order::query()
                ->where('payment_reference', 'fedapay_tx_' . $transactionId)
                ->first();
        } else {
            $order = Order::find($orderId);
        }

        if (! $order) {
            return response()->json(['message' => 'Order not found'], 404);
        }

        $gatewayStatus = [
            'raw_status' => strtolower((string) (
                data_get($payload, 'data.transaction.status')
                ?? data_get($payload, 'transaction.status')
                ?? ''
            )),
            'reference' => data_get($payload, 'data.transaction.reference')
                ?? data_get($payload, 'transaction.reference'),
            'is_paid' => false,
            'is_failed' => false,
        ];

        $gatewayStatus['is_paid'] = in_array($gatewayStatus['raw_status'], ['approved', 'paid', 'completed', 'successful', 'success'], true);
        $gatewayStatus['is_failed'] = in_array($gatewayStatus['raw_status'], ['failed', 'canceled', 'cancelled', 'declined', 'expired'], true);

        $deleted = $this->applyGatewayStatus($order, $gatewayStatus);

        if (! $deleted) {
            $order->load('user');
        }
        if (! $deleted && $order->user) {
            if ($order->payment_status === Order::PAYMENT_PAID) {
                $this->notifications->notifyOrderStatus($order->user, $order->id, $order->number, 'Payée');
            } elseif ($order->payment_status === Order::PAYMENT_FAILED) {
                $this->notifications->notifyOrderStatus($order->user, $order->id, $order->number, 'Paiement échoué');
            }
        }

        return response()->json(['message' => 'OK']);
    }

    /**
     * Simulation : marquer une commande comme payée (pour tests sans Fedapay).
     */
    public function simulate(Request $request): JsonResponse
    {
        $orderId = $request->query('order_id');
        if (! $orderId) {
            return response()->json(['message' => 'Missing order_id'], 400);
        }
        $order = Order::find($orderId);
        if (! $order) {
            return response()->json(['message' => 'Order not found'], 404);
        }
        $order->update([
            'payment_status' => Order::PAYMENT_PAID,
            'status' => Order::STATUS_PAID,
            'paid_at' => now(),
            'payment_reference' => 'simulate-' . uniqid(),
        ]);
        $order->refresh()->ensureInvoiceNumber();
        $this->notifyAdminIfEmplettePaid($order->fresh());

        $order->load('user');
        if ($order->user) {
            $this->notifications->notifyOrderStatus($order->user, $order->id, $order->number, 'Payée');
        }

        return response()->json([
            'message' => __('Payment successful.'),
            'order_id' => $order->id,
        ]);
    }

    /** @param array{raw_status:?string,reference:?string,is_paid:bool,is_failed:bool} $gatewayStatus */
    private function applyGatewayStatus(Order $order, array $gatewayStatus): bool
    {
        $wasPaid = $order->payment_status === Order::PAYMENT_PAID;

        if ($gatewayStatus['is_paid']) {
            $order->update([
                'payment_status' => Order::PAYMENT_PAID,
                'status' => Order::STATUS_PAID,
                'paid_at' => $order->paid_at ?? now(),
                'payment_reference' => $gatewayStatus['reference'] ?: $order->payment_reference,
            ]);
            $order->refresh()->ensureInvoiceNumber();
            if (! $wasPaid) {
                $this->notifyAdminIfEmplettePaid($order->fresh());
            }

            Emplette::query()
                ->where('order_id', $order->id)
                ->update(['status' => Emplette::STATUS_CONVERTED]);

            return false;
        }

        if ($gatewayStatus['is_failed'] && $order->payment_status !== Order::PAYMENT_PAID) {
            // Règle métier: si paiement échoué, la commande ne doit pas exister.
            Emplette::query()
                ->where('order_id', $order->id)
                ->update([
                    'status' => Emplette::STATUS_ACCEPTED,
                    'order_id' => null,
                ]);
            $order->delete();

            return true;
        }

        if ($gatewayStatus['reference']) {
            $order->update([
                'payment_reference' => $gatewayStatus['reference'],
            ]);
        }

        Log::info('Fedapay status received without terminal state', [
            'order_id' => $order->id,
            'raw_status' => $gatewayStatus['raw_status'],
        ]);

        return false;
    }

    private function notifyAdminIfEmplettePaid(Order $order): void
    {
        $emplette = Emplette::query()
            ->where('order_id', $order->id)
            ->first();

        if (! $emplette) {
            return;
        }

        $this->adminMails->sendEmplettePayment($emplette, $order);
    }
}
