<?php

declare(strict_types=1);

namespace App\Services;

use App\Enums\EventStatus;
use App\Enums\OrderStatus;
use App\Enums\TicketCapacityStrategy;
use App\Models\Attendee;
use App\Models\Event;
use App\Models\Order;
use App\Models\TicketType;

/**
 * Datos agregados para la vista de asistentes / pedidos por evento (panel organizador).
 *
 * Comisión plataforma documentada en {@see FeeCalculator}.
 */
final class EventAttendeesOverviewBuilder
{
    public function build(Event $event): array
    {
        $event->loadMissing(['venue', 'category', 'ticketTypes']);

        $tz = $event->timezone ?: config('app.timezone');
        $ticketTypes = $event->ticketTypes->sortBy('id')->values();

        $meta = is_array($event->metadata) ? $event->metadata : [];
        $primaryRaw = $meta['primary_type'] ?? null;
        $contentTypeLabel = $primaryRaw !== null && $primaryRaw !== ''
            ? ucfirst(str_replace('_', ' ', (string) $primaryRaw))
            : ($event->category?->name ?? __('Evento'));

        $startLabel = $event->start_date !== null
            ? $event->start_date->timezone($tz)->translatedFormat('d \d\e F \d\e Y')
            : '—';

        $publicUrl = null;
        if ($event->status === EventStatus::Published && $event->slug !== '') {
            try {
                $publicUrl = route('landing.event.show', $event);
            } catch (\Throwable) {
                $publicUrl = null;
            }
        }

        $ticketIds = $ticketTypes->pluck('id')->all();

        $countsByTicketStatus = [];
        if ($ticketIds !== []) {
            $rows = Attendee::query()
                ->whereIn('attendees.ticket_type_id', $ticketIds)
                ->join('orders', 'orders.id', '=', 'attendees.order_id')
                ->selectRaw('attendees.ticket_type_id as tid, orders.status as st, COUNT(*) as c')
                ->groupBy('attendees.ticket_type_id', 'orders.status')
                ->get();
            foreach ($rows as $row) {
                $countsByTicketStatus[(int) $row->tid][$row->st] = (int) $row->c;
            }
        }

        $ordersQuery = Order::query()
            ->whereHas('attendees', static function ($q) use ($event): void {
                $q->whereHas('ticketType', static fn ($q2) => $q2->where('event_id', $event->id));
            });

        $orderAgg = (clone $ordersQuery)
            ->selectRaw(
                'orders.status, COUNT(*) as order_count, COALESCE(SUM(orders.total_amount), 0) as sum_total, COALESCE(SUM(orders.platform_fee), 0) as sum_fee, COALESCE(SUM(orders.net_organizer_amount), 0) as sum_net'
            )
            ->groupBy('orders.status')
            ->get()
            ->keyBy(static fn (Order $r) => $r->status->value);

        $issuedCompleted = Attendee::query()
            ->whereHas('ticketType', static fn ($q) => $q->where('event_id', $event->id))
            ->whereHas('order', static fn ($q) => $q->where('status', OrderStatus::Completed))
            ->count();

        $checkedIn = Attendee::query()
            ->whereHas('ticketType', static fn ($q) => $q->where('event_id', $event->id))
            ->whereHas('order', static fn ($q) => $q->where('status', OrderStatus::Completed))
            ->whereNotNull('checked_in_at')
            ->count();

        $pendingCheckin = max(0, $issuedCompleted - $checkedIn);

        $pctIn = $issuedCompleted > 0 ? (int) round(100 * $checkedIn / $issuedCompleted) : 0;

        $currency = Order::query()
            ->whereHas('attendees', static function ($q) use ($event): void {
                $q->whereHas('ticketType', static fn ($q2) => $q2->where('event_id', $event->id));
            })
            ->value('currency') ?? 'USD';

        $ticketSummaries = [];
        foreach ($ticketTypes as $tt) {
            $perStatus = $countsByTicketStatus[$tt->id] ?? [];
            $completedQty = (int) ($perStatus[OrderStatus::Completed->value] ?? 0);
            $issued = max($completedQty, (int) $tt->quantity_sold);

            $capacityInfo = $this->capacityAvailability($tt);

            $ticketSummaries[] = [
                'id' => $tt->id,
                'name' => $tt->name,
                'sku_label' => '#'.$tt->id,
                'issued' => $issued,
                'available' => $capacityInfo['available'],
                'capacity' => $capacityInfo['capacity'],
                'unlimited' => $capacityInfo['unlimited'],
                'shared_pool_note' => $capacityInfo['shared_pool_note'],
                'ticket_kind_label' => $this->ticketKindLabel((string) ($tt->ticket_kind ?? 'standard')),
                'base_price' => (float) ($tt->sale_price ?? $tt->price),
                'counts_by_status' => [
                    'pending' => (int) ($perStatus[OrderStatus::Pending->value] ?? 0),
                    'completed' => (int) ($perStatus[OrderStatus::Completed->value] ?? 0),
                    'refunded' => (int) ($perStatus[OrderStatus::Refunded->value] ?? 0),
                ],
            ];
        }

        $groupedTickets = collect($ticketSummaries)->groupBy('ticket_kind_label');

        $sumRow = static function (string $status) use ($orderAgg): array {
            $row = $orderAgg->get($status);

            return [
                'orders' => $row ? (int) $row->order_count : 0,
                'total_amount' => $row ? (float) $row->sum_total : 0.0,
                'platform_fee' => $row ? (float) $row->sum_fee : 0.0,
                'organizer_net' => $row ? (float) $row->sum_net : 0.0,
            ];
        };

        $pendingAgg = $sumRow(OrderStatus::Pending->value);
        $completedAgg = $sumRow(OrderStatus::Completed->value);
        $refundedAgg = $sumRow(OrderStatus::Refunded->value);

        $totalOrdersAmount = $pendingAgg['total_amount'] + $completedAgg['total_amount'] + $refundedAgg['total_amount'];
        $totalOrdersCount = $pendingAgg['orders'] + $completedAgg['orders'] + $refundedAgg['orders'];

        return [
            'currency' => $currency,
            'fee_explainer' => __('RegiTicket retiene :fixed más el :pct del precio base por entrada (comisión por boleto).', [
                'fixed' => '$'.number_format(FeeCalculator::fixedFee(), 2),
                'pct' => rtrim(rtrim(number_format(FeeCalculator::percentageFee() * 100, 4, '.', ''), '0'), '.').'%',
            ]),
            'fee_formula_short' => '$'.number_format(FeeCalculator::fixedFee(), 2).' + '.rtrim(rtrim(number_format(FeeCalculator::percentageFee() * 100, 4, '.', ''), '0'), '.').'%',
            'event' => [
                'title' => $event->title,
                'start_label' => $startLabel,
                'venue_name' => $event->venue?->name,
                'content_type_label' => $contentTypeLabel,
                'public_url' => $publicUrl,
            ],
            'ticket_groups' => $groupedTickets->map(static fn ($items, $label) => [
                'label' => $label,
                'tickets' => $items->values()->all(),
            ])->values()->all(),
            'attendance' => [
                'entrada_lines_total' => $issuedCompleted,
                'checked_in' => $checkedIn,
                'pending_checkin' => $pendingCheckin,
                'checkin_pct' => $pctIn,
                'orders_completed' => $completedAgg['orders'],
                'orders_pending' => $pendingAgg['orders'],
                'orders_refunded' => $refundedAgg['orders'],
            ],
            'sales' => [
                'pending' => $pendingAgg,
                'completed' => $completedAgg,
                'refunded' => $refundedAgg,
                'grand_total_amount' => $totalOrdersAmount,
                'grand_order_count' => $totalOrdersCount,
            ],
        ];
    }

    /**
     * @return array{available: ?int, capacity: ?int, unlimited: bool, shared_pool_note: ?string}
     */
    private function capacityAvailability(TicketType $tt): array
    {
        if ($tt->capacity_strategy === TicketCapacityStrategy::Unlimited) {
            return [
                'available' => null,
                'capacity' => null,
                'unlimited' => true,
                'shared_pool_note' => null,
            ];
        }

        if ($tt->capacity_strategy === TicketCapacityStrategy::SharedPool) {
            return [
                'available' => null,
                'capacity' => $tt->capacity,
                'unlimited' => false,
                'shared_pool_note' => __('Cupo compartido entre entradas; revisa el editor del evento para el límite global.'),
            ];
        }

        $cap = $tt->effectiveCapacity();
        if ($cap === null) {
            return [
                'available' => null,
                'capacity' => null,
                'unlimited' => true,
                'shared_pool_note' => null,
            ];
        }

        $sold = (int) $tt->quantity_sold;

        return [
            'available' => max(0, $cap - $sold),
            'capacity' => $cap,
            'unlimited' => false,
            'shared_pool_note' => null,
        ];
    }

    private function ticketKindLabel(string $kind): string
    {
        return match ($kind) {
            'standard' => __('Entradas estándar'),
            'vip' => 'VIP',
            default => ucfirst(str_replace('_', ' ', $kind)),
        };
    }
}
