<?php

namespace App\Http\Controllers\Dashboard;

use App\Enums\OrderStatus;
use App\Mail\OrderConfirmationMail;
use App\Models\Order;
use App\Models\Role;
use App\Models\Scopes\OrganizerScope;
use App\Models\User;
use App\Services\AuditLogger;
use App\Services\FeeCalculator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\View\View;

/**
 * Panel de **órdenes / pagos**: listado para cliente u organizador/staff, detalle JSON y reenvío de confirmación.
 *
 * El alcance de datos depende del rol (global scope de organizador + políticas).
 */
final class PaymentsController
{
    public function __construct(
        private readonly AuditLogger $auditLogger,
    ) {}

    public function index(Request $request): View
    {
        $user = $request->user();
        abort_unless($user, 403);

        $status = $request->string('status')->toString();

        $q = Order::query()->with([
            'transactions',
            'attendees.ticketType.event',
            'user',
            'organizer.user',
        ]);

        if ($user->hasRole(Role::SLUG_CLIENTE)) {
            $q->withoutGlobalScope(OrganizerScope::class)
                ->where('user_id', $user->id);
        }

        if ($status !== '') {
            $q->where('status', $status);
        }

        // Listado en cliente con Bootstrap Table (10/20/…); cap para no saturar memoria.
        $orders = $q->latest('id')->limit(1000)->get();

        $stats = [
            'pending' => (clone $q)->where('status', OrderStatus::Pending->value)->count(),
            'completed' => (clone $q)->where('status', OrderStatus::Completed->value)->count(),
            'refunded' => (clone $q)->where('status', OrderStatus::Refunded->value)->count(),
        ];

        return view('dashboard.payments.index', [
            'orders' => $orders,
            'stats' => $stats,
            'status' => $status,
        ]);
    }

    public function detail(Request $request, Order $order): JsonResponse
    {
        $user = $request->user();
        abort_unless($user, 403);

        $this->authorizeOrganizerStaff($user, $order);

        $order->load([
            'user',
            'organizer.user',
            'transactions',
            'attendees.ticketType.event.venue',
        ]);

        return response()->json($this->orderDetailPayload($order));
    }

    public function resendTickets(Request $request, Order $order): JsonResponse
    {
        $user = $request->user();
        abort_unless($user, 403);

        $this->authorizeOrganizerStaff($user, $order);

        if ($order->status !== OrderStatus::Completed) {
            return response()->json([
                'message' => __('Solo se pueden reenviar entradas de órdenes completadas.'),
            ], 422);
        }

        $order->load(['attendees.ticketType.event.venue', 'user', 'organizer']);

        $recipient = $order->user?->email ?: $order->attendees->first()?->holder_email;

        if (! $recipient) {
            return response()->json([
                'message' => __('No hay un correo de destinatario para esta orden.'),
            ], 422);
        }

        try {
            Mail::to($recipient)->send(new OrderConfirmationMail($order));
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'message' => __('No se pudo enviar el correo. Revisa la configuración de correo e intenta de nuevo.'),
            ], 500);
        }

        $this->auditLogger->log(
            action: 'payments.tickets_resent',
            module: 'payments',
            summary: 'Order confirmation email resent',
            context: [
                'order_id' => $order->id,
                'order_uuid' => $order->uuid,
                'recipient' => $recipient,
            ],
            subject: $order,
            request: $request,
        );

        return response()->json([
            'message' => __('Correo con las entradas y códigos QR enviado a :email.', ['email' => $recipient]),
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function orderDetailPayload(Order $order): array
    {
        $tz = config('app.timezone');

        $event = $order->attendees->first()?->ticketType?->event;

        $buyerUser = $order->user;

        $organizerUser = $order->organizer?->user;

        return [
            'order' => [
                'id' => $order->id,
                'uuid' => $order->uuid,
                'status' => $order->status->value,
                'currency' => $order->currency ?? 'USD',
                'total_amount' => (string) $order->total_amount,
                'platform_fee' => (string) $order->platform_fee,
                'net_organizer_amount' => (string) $order->net_organizer_amount,
                'created_at' => $order->created_at?->timezone($tz)->toIso8601String(),
                'completed_at' => $order->completed_at?->timezone($tz)->toIso8601String(),
            ],
            'buyer' => [
                'account' => $buyerUser ? [
                    'id' => $buyerUser->id,
                    'name' => $buyerUser->name,
                    'email' => $buyerUser->email,
                    'phone' => $buyerUser->phone,
                    'organization_name' => $buyerUser->organization_name,
                ] : null,
                'primary_contact_email' => $buyerUser?->email ?? $order->attendees->first()?->holder_email,
            ],
            'organizer' => [
                'id' => $order->organizer_id,
                'account' => $organizerUser ? [
                    'name' => $organizerUser->name,
                    'email' => $organizerUser->email,
                ] : null,
            ],
            'event' => $event ? [
                'id' => $event->id,
                'title' => $event->title,
                'slug' => $event->slug,
                'start_date' => $event->start_date,
                'timezone' => $event->timezone ?? null,
                'venue' => $event->venue ? [
                    'name' => $event->venue->name,
                    'city' => $event->venue->city ?? null,
                    'country' => $event->venue->country ?? null,
                ] : null,
            ] : null,
            'transactions' => $order->transactions->sortByDesc('id')->values()->map(static function ($tx) {
                return [
                    'id' => $tx->id,
                    'gateway' => $tx->gateway?->value,
                    'status' => $tx->status?->value,
                    'reference_id' => $tx->reference_id,
                    'amount' => $tx->amount !== null ? (string) $tx->amount : null,
                    'currency' => $tx->currency,
                    'error_code' => $tx->error_code,
                    'error_message' => $tx->error_message,
                    'payload' => $tx->payload,
                    'created_at' => $tx->created_at?->toIso8601String(),
                ];
            })->all(),
            'attendees' => $order->attendees->map(static function ($a) {
                $base = (float) ($a->ticketType?->sale_price ?? $a->ticketType?->price ?? 0);

                return [
                    'id' => $a->id,
                    'holder_name' => $a->holder_name,
                    'holder_email' => $a->holder_email,
                    'security_code' => $a->security_code,
                    'registration_data' => $a->registration_data ?? [],
                    'checked_in_at' => $a->checked_in_at?->toIso8601String(),
                    'ticket_type' => $a->ticketType ? [
                        'id' => $a->ticketType->id,
                        'name' => $a->ticketType->name,
                        'sku' => $a->ticketType->sku,
                        'price_base' => (string) ($a->ticketType->sale_price ?? $a->ticketType->price ?? '0'),
                        'line_total_charged' => (string) FeeCalculator::calculateTotalForAttendee($base),
                    ] : null,
                ];
            })->values()->all(),
        ];
    }

    private function authorizeOrganizerStaff(User $user, Order $order): void
    {
        if ($user->hasRole(Role::SLUG_SUPER_ADMIN) || $user->hasRole(Role::SLUG_ADMINISTRADOR)) {
            return;
        }

        if ($user->hasRole(Role::SLUG_CLIENTE)
            && $order->user_id !== null
            && (int) $order->user_id === (int) $user->id) {
            return;
        }

        $organizerId = $user->organizer?->id;

        if ($organizerId === null || (int) $order->organizer_id !== (int) $organizerId) {
            abort(403);
        }
    }
}
