<?php

namespace App\Services\Stripe;

use App\Models\FormSubmission;
use InvalidArgumentException;

class PaymentAmountResolver
{
    /**
     * Resolve charge amounts from stored registration data (authoritative server-side).
     *
     * @return array{subtotal: float, tax: float, total: float, amount_cents: int, currency: string, is_setup: bool}
     */
    public function resolveFromSubmission(FormSubmission $submission, ?string $currencyHint = 'usd'): array
    {
        $data = $submission->submission_data ?? [];

        return $this->resolveFromSubmissionData(is_array($data) ? $data : [], $currencyHint);
    }

    /**
     * @param  array<int|string, mixed>  $data
     * @return array{subtotal: float, tax: float, total: float, amount_cents: int, currency: string, is_setup: bool}
     */
    public function resolveFromSubmissionData(array $data, ?string $currencyHint = 'usd'): array
    {

        $classDetails = $this->extractClassDetails($data);
        $paymentStep = $this->extractPaymentStep($data);

        $currency = strtolower((string) ($classDetails['currency'] ?? $currencyHint ?? 'usd'));

        // Checkout form stores the exact amount the customer pays — do not recalculate tax on top.
        $explicitDue = null;
        if (isset($paymentStep['amount_due_now']) && (float) $paymentStep['amount_due_now'] > 0) {
            $explicitDue = (float) $paymentStep['amount_due_now'];
        } elseif (isset($paymentStep['final_price']) && (float) $paymentStep['final_price'] > 0) {
            $explicitDue = (float) $paymentStep['final_price'];
        }

        if ($explicitDue !== null) {
            $total = round($explicitDue, 2);
            $taxPercent = (float) ($classDetails['invoice_tax_value'] ?? 0);
            if ($taxPercent > 0) {
                $subtotal = round($total / (1 + ($taxPercent / 100)), 2);
                $tax = round($total - $subtotal, 2);
            } else {
                $subtotal = $total;
                $tax = 0.0;
            }

            $isSetup = $total <= 0;

            return [
                'subtotal' => $subtotal,
                'tax' => $tax,
                'total' => $total,
                'amount_cents' => $isSetup ? 0 : (int) round($total * 100),
                'currency' => $currency,
                'is_setup' => $isSetup,
            ];
        }

        $subtotal = 0.0;
        if (isset($classDetails['amount_due_now'])) {
            $subtotal = (float) $classDetails['amount_due_now'];
        } elseif (isset($classDetails['price'])) {
            $subtotal = (float) $classDetails['price'];
            $discount = (float) ($paymentStep['discount_amount'] ?? 0);
            if ($discount > 0) {
                $subtotal = max(0, $subtotal - $discount);
            }
        }

        $taxPercent = (float) ($classDetails['invoice_tax_value'] ?? 0);
        $tax = 0.0;
        $total = $subtotal;

        if ($taxPercent > 0 && $subtotal > 0) {
            $basePrice = (float) ($classDetails['price'] ?? $subtotal);
            $discount = (float) ($paymentStep['discount_amount'] ?? 0);
            if ($discount > 0) {
                $basePrice = max(0, $basePrice - $discount);
            }
            $tax = round($basePrice * ($taxPercent / 100), 2);
            $total = round($basePrice + $tax, 2);
            $subtotal = round($basePrice, 2);
        }

        $isSetup = $total <= 0;

        return [
            'subtotal' => round($subtotal, 2),
            'tax' => round($tax, 2),
            'total' => round($total, 2),
            'amount_cents' => $isSetup ? 0 : (int) round($total * 100),
            'currency' => $currency,
            'is_setup' => $isSetup,
        ];
    }

    public function assertClientAmountMatches(array $resolved, ?float $clientAmount): void
    {
        if ($clientAmount === null) {
            return;
        }

        if ($resolved['is_setup']) {
            return;
        }

        if (abs($resolved['total'] - (float) $clientAmount) > 0.02) {
            throw new InvalidArgumentException('Payment amount does not match the registration total.');
        }
    }

    private function extractClassDetails(array $data): array
    {
        foreach ($this->iterateSteps($data) as $item) {
            if (!is_array($item)) {
                continue;
            }
            if (isset($item['class_details']) && is_array($item['class_details'])) {
                return $item['class_details'];
            }
            if (isset($item['group_id']) || isset($item['group_name'])) {
                return $item;
            }
        }

        if (isset($data['class_details']) && is_array($data['class_details'])) {
            return $data['class_details'];
        }

        return [];
    }

    private function extractPaymentStep(array $data): array
    {
        foreach ($this->iterateSteps($data) as $item) {
            if (!is_array($item)) {
                continue;
            }
            if (isset($item['final_price']) || isset($item['payment_method']) || ($item['step_3'] ?? null) === 'payment_details') {
                return $item;
            }
        }

        return is_array($data['payment_details'] ?? null) ? $data['payment_details'] : [];
    }

    private function iterateSteps(array $data): array
    {
        if ($this->isListLike($data)) {
            return $data;
        }

        return array_values($data);
    }

    private function isListLike(array $data): bool
    {
        if ($data === []) {
            return true;
        }

        return array_keys($data) === range(0, count($data) - 1);
    }
}
