<?php

namespace App\Domains\Purchase\Resources;

use App\Domains\Purchase\Models\Purchase;
use App\Domains\Purchase\Models\PurchaseItem;
use App\Domains\Purchase\Models\TelepagosPaymentCandidate;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

/**
 * @mixin Purchase
 */
class PurchaseResource extends JsonResource
{
    /**
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        $items = $this->resource->relationLoaded('items')
            ? $this->resource->getRelation('items')
            : collect();
        $ticketsCount = array_key_exists('tickets_count', $this->resource->getAttributes())
            ? (int) $this->resource->getAttribute('tickets_count')
            : null;
        $paymentVerification = $this->resolvePaymentVerification();

        $subtotal = $items->isNotEmpty()
            ? $items->reduce(
                fn (float $carry, PurchaseItem $item): float => $carry + $this->resolveItemSubtotal($item),
                0.0,
            )
            : (float) ($this->total ?? 0);

        $total = $this->status === Purchase::STATUS_PAID && $this->total !== null
            ? (float) $this->total
            : ($items->isNotEmpty()
                ? $items->reduce(
                    fn (float $carry, PurchaseItem $item): float => $carry + $this->resolveItemTotal($item),
                    0.0,
                )
                : (float) ($this->total ?? 0));

        return [
            'id' => $this->id,
            'cart_id' => $this->cart_id,
            'tenant_codigo' => $this->tenant_codigo,
            'user_id' => $this->user_id,
            'created_at' => $this->created_at,
            'status' => $this->status,
            'payment_method' => $this->payment_method,
            'expires_at' => $this->expires_at,
            'dni' => $this->dni,
            'transfer_payer_dni' => $this->transfer_payer_dni,
            'telefono' => $this->telefono,
            'nombre_apellido' => $this->nombre_apellido,
            'email' => $this->email,
            'items_source' => $items->isNotEmpty() ? 'purchase' : null,
            'items' => PurchaseItemResource::collection($items),
            'tickets_count' => $this->when($ticketsCount !== null, $ticketsCount),
            'has_generated_tickets' => $this->when($ticketsCount !== null, $ticketsCount > 0),
            'payment_verification' => $this->when($paymentVerification !== null, $paymentVerification),
            'subtotal' => $this->formatMoney($subtotal),
            'total' => $this->formatMoney($total),
        ];
    }

    protected function resolveItemSubtotal(PurchaseItem $item): float
    {
        return (float) $item->precio_unitario * $item->cantidad;
    }

    protected function resolveItemTotal(PurchaseItem $item): float
    {
        return (float) ($item->total ?? 0);
    }

    protected function formatMoney(float|int|string|null $amount): string
    {
        return number_format((float) ($amount ?? 0), 2, '.', '');
    }

    /** @return array<string, mixed>|null */
    private function resolvePaymentVerification(): ?array
    {
        if (
            $this->status !== Purchase::STATUS_IN_REVIEW
            || $this->payment_method !== 'transfer'
            || ! $this->resource->relationLoaded('telepagosPaymentCandidates')
        ) {
            return null;
        }

        $candidates = $this->resource
            ->getRelation('telepagosPaymentCandidates')
            ->sort(fn (TelepagosPaymentCandidate $left, TelepagosPaymentCandidate $right): int => $this->comparePaymentCandidates($left, $right))
            ->values();
        /** @var TelepagosPaymentCandidate|null $primary */
        $primary = $candidates->first();

        return [
            'status' => $primary === null ? 'pending' : 'candidate',
            'candidate_count' => $candidates->count(),
            'primary' => $primary === null ? null : [
                'reason' => $primary->match_reason,
                'dni_distance' => $primary->dni_distance,
                'payment_amount' => $this->formatMoney($primary->payment_amount),
                'purchase_amount' => $this->formatMoney($primary->purchase_amount),
                'amount_difference' => $this->formatMoney($primary->amount_difference),
                'confidence' => $primary->confidence,
                'detected_at' => $primary->payment?->created_at?->toIso8601String(),
            ],
            'reasons' => $candidates
                ->pluck('match_reason')
                ->unique()
                ->values()
                ->all(),
        ];
    }

    private function comparePaymentCandidates(
        TelepagosPaymentCandidate $left,
        TelepagosPaymentCandidate $right,
    ): int {
        $reasonComparison = $this->paymentCandidateRank($left->match_reason)
            <=> $this->paymentCandidateRank($right->match_reason);

        if ($reasonComparison !== 0) {
            return $reasonComparison;
        }

        $differenceComparison = (float) $left->amount_difference <=> (float) $right->amount_difference;

        if ($differenceComparison !== 0) {
            return $differenceComparison;
        }

        $leftTimestamp = $left->payment?->created_at?->getTimestamp() ?? 0;
        $rightTimestamp = $right->payment?->created_at?->getTimestamp() ?? 0;

        return ($rightTimestamp <=> $leftTimestamp) ?: ($right->id <=> $left->id);
    }

    private function paymentCandidateRank(string $reason): int
    {
        return match ($reason) {
            'ambiguous_exact_match' => 0,
            'exact_dni_near_amount' => 1,
            'exact_amount_different_dni' => 2,
            default => 3,
        };
    }
}
