<?php

namespace App\Services;

/**
 * Resolves registration pricing fields for emails, invoices, and thank-you pages.
 */
class RegistrationPricingHelper
{
    /** @var list<string> */
    private const SUBMISSION_MONEY_KEYS = [
        'final_price',
        'amount_due_now',
        'total_class_value',
        'total_program_price',
        'discount_amount',
        'per_unit_price',
        'price',
        'amount',
        'tax',
        'subtotal',
        'total',
    ];

    /**
     * @param  array<string, mixed>  $data  Flattened or stepped submission + class_details
     * @return array<string, mixed>
     */
    public function enrich(array $data): array
    {
        $perUnitPrice = $this->toFloat($this->extract($data, [
            'per_unit_price',
            '3.per_unit_price',
            'class_details.price',
            'class_details.value',
            '3.class_details.price',
            '3.class_details.value',
            'price',
        ]));

        $lessonCount = max(1, (int) $this->extract($data, [
            'number_of_lessons',
            'class_details.number_of_lessons',
            '3.class_details.number_of_lessons',
        ], '1'));

        $discount = $this->toFloat($this->extract($data, [
            'discount_amount',
            '3.discount_amount',
        ]));

        $storedTotalClass = $this->toFloat($this->extract($data, [
            'total_class_value',
            'class_details.total_class_value',
            '3.class_details.total_class_value',
        ]));

        $totalClassValue = $storedTotalClass > 0
            ? $storedTotalClass
            : max(0, ($perUnitPrice * $lessonCount) - $discount);

        $amountDueNow = $this->toFloat($this->extract($data, [
            'amount_due_now',
            '3.amount_due_now',
            'final_price',
            '3.final_price',
            'amount_raw',
        ]));

        $frequency = strtolower(trim((string) $this->extract($data, [
            'payment_charges_frequency',
            'class_details.payment_charges_frequency',
            '3.class_details.payment_charges_frequency',
        ], 'per_lesson')));

        $installmentNumber = (int) $this->extract($data, ['installment_number', '3.installment_number'], '0');
        $installmentTotal = (int) $this->extract($data, ['installment_total', '3.installment_total'], '0');

        $currency = strtoupper(trim((string) ($data['currency'] ?? 'USD')));

        return array_merge($data, [
            'per_unit_price' => $perUnitPrice,
            'per_unit_price_raw' => (string) $perUnitPrice,
            'number_of_lessons' => (string) $lessonCount,
            'total_class_value' => $this->formatMoney($totalClassValue, $currency),
            'total_class_value_raw' => (string) $totalClassValue,
            'total_program_price' => $this->formatMoney($totalClassValue, $currency),
            'total_program_price_raw' => (string) $totalClassValue,
            'price_per_lesson' => $this->formatMoney($perUnitPrice, $currency),
            'price_per_lesson_display' => $this->formatMoney($perUnitPrice, $currency).' '.$this->perLessonSuffix($data),
            'payment_charges_frequency' => $frequency,
            'payment_frequency_label' => $this->formatFrequencyLabel($frequency),
            'installment_label' => $this->formatInstallmentLabel($installmentNumber, $installmentTotal),
            'amount_due_now' => $amountDueNow > 0 ? $this->formatMoney($amountDueNow, $currency) : '',
            'amount_due_now_raw' => (string) $amountDueNow,
        ]);
    }

    /**
     * Remove duplicate / internal keys from email field maps (label => value).
     *
     * @param  array<string, string>  $rows
     * @return array<string, string>
     */
    public function deduplicateDisplayFields(array $rows): array
    {
        $normalized = [];
        foreach ($rows as $label => $value) {
            $key = strtolower(trim($label));
            if (
                str_ends_with($key, ' raw') ||
                str_contains($key, '_raw') ||
                in_array($key, [
                    'step 3', 'step 1', 'step 2', 'step 4',
                    'payment confirmation body',
                    'raw',
                ], true)
            ) {
                continue;
            }
            $normalized[$label] = $value;
        }

        if (isset($normalized['Amount Due Now'])) {
            unset($normalized['Final Price']);
        }
        if (isset($normalized['Total Class Value'])) {
            unset($normalized['Total Program Price']);
        }
        if (isset($normalized['Payment Frequency Label'])) {
            unset($normalized['Payment Charges Frequency']);
        }
        if (isset($normalized['Price Per Lesson Display'])) {
            unset($normalized['Price Per Lesson'], $normalized['Per Unit Price']);
        }

        return $normalized;
    }

    /**
     * Canonical payment rows for booking emails (no duplicates).
     *
     * @param  array<string, mixed>  $enriched
     * @return array<string, string>
     */
    public function canonicalPaymentRows(array $enriched, bool $isPaid = false): array
    {
        $rows = [];

        $amountKey = $isPaid ? 'Amount Paid' : 'Amount Due Now';
        $amount = (string) ($enriched['amount'] ?? $enriched['amount_due_now'] ?? '');
        if ($amount !== '') {
            $rows[$amountKey] = $amount;
        }

        if (! empty($enriched['total_class_value'])) {
            $rows['Total Value of the Class'] = (string) $enriched['total_class_value'];
        }

        if (! empty($enriched['price_per_lesson_display'])) {
            $rows['Price'] = (string) $enriched['price_per_lesson_display'];
        }

        if (! empty($enriched['payment_frequency_label'])) {
            $rows['Payment Frequency'] = (string) $enriched['payment_frequency_label'];
        }

        if (! empty($enriched['installment_label'])) {
            $rows['Installment'] = (string) $enriched['installment_label'];
        }

        $discount = $this->toFloat($enriched['discount_amount'] ?? $enriched['3.discount_amount'] ?? '0');
        if ($discount > 0) {
            $currency = strtoupper(trim((string) ($enriched['currency'] ?? 'USD')));
            $rows['Discount'] = '-'.$this->formatMoney($discount, $currency);
        }

        $coupon = trim((string) ($enriched['coupon_code'] ?? $enriched['3.coupon_code'] ?? ''));
        if ($coupon !== '') {
            $rows['Coupon'] = $coupon;
        }

        $method = trim((string) ($enriched['payment_method'] ?? ''));
        if ($method !== '') {
            $rows['Payment Method'] = ucfirst($method);
        }

        return $rows;
    }

    /**
     * Strip raw payment fields and merge canonical payment summary.
     *
     * @param  array<string, string>  $rows
     * @return array<string, string>
     */
    public function mergeCanonicalPaymentFields(array $rows, array $enriched, bool $isPaid = false): array
    {
        $filtered = [];
        foreach ($rows as $label => $value) {
            $lower = strtolower($label);
            if ($this->isPaymentRelatedLabel($lower)) {
                continue;
            }
            $filtered[$label] = $value;
        }

        return array_merge($filtered, $this->canonicalPaymentRows($enriched, $isPaid));
    }

    /**
     * Normalize monetary fields in registration form_data (steps + class_details).
     *
     * @param  array<int|string, mixed>  $submissionData
     * @return array<int|string, mixed>
     */
    public function sanitizeSubmissionData(array $submissionData): array
    {
        if ($this->isListLike($submissionData)) {
            $out = [];
            foreach ($submissionData as $item) {
                $out[] = is_array($item) ? $this->sanitizeSubmissionStep($item) : $item;
            }

            return $out;
        }

        return $this->sanitizeSubmissionStep($submissionData);
    }

    private function isPaymentRelatedLabel(string $lowerLabel): bool
    {
        $needles = [
            'price', 'amount', 'payment', 'discount', 'coupon', 'fee',
            'total class', 'total program', 'final', 'installment', 'frequency',
        ];
        foreach ($needles as $needle) {
            if (str_contains($lowerLabel, $needle)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  array<string, mixed>  $data
     * @param  list<string>  $paths
     */
    private function extract(array $data, array $paths, string $default = ''): string
    {
        foreach ($paths as $path) {
            if (array_key_exists($path, $data) && $data[$path] !== null && $data[$path] !== '') {
                return is_scalar($data[$path]) ? (string) $data[$path] : $default;
            }
        }

        return $default;
    }

    private function toFloat(mixed $value): float
    {
        return $this->roundAmount($value);
    }

    private function roundAmount(float|int|string|null $amount, int $decimals = 2): float
    {
        if ($amount === null || $amount === '') {
            return 0.0;
        }

        if (! is_numeric($amount)) {
            $cleaned = preg_replace('/[^0-9.\-]/', '', (string) $amount);

            if (! is_numeric($cleaned)) {
                return 0.0;
            }

            $amount = $cleaned;
        }

        return round((float) $amount, $decimals);
    }

    /**
     * @param  array<string, mixed>  $step
     * @return array<string, mixed>
     */
    private function sanitizeSubmissionStep(array $step): array
    {
        foreach (self::SUBMISSION_MONEY_KEYS as $key) {
            if (array_key_exists($key, $step) && is_numeric($step[$key])) {
                $step[$key] = $this->roundAmount($step[$key]);
            }
        }

        if (isset($step['class_details']) && is_array($step['class_details'])) {
            $step['class_details'] = $this->sanitizeSubmissionStep($step['class_details']);
        }

        return $step;
    }

    /**
     * @param  array<int|string, mixed>  $data
     */
    private function isListLike(array $data): bool
    {
        if ($data === []) {
            return true;
        }

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

    private function formatMoney(float $amount, string $currency): string
    {
        $symbol = match (strtoupper($currency)) {
            'USD' => '$',
            'EUR' => '€',
            'GBP' => '£',
            default => strtoupper($currency).' ',
        };

        $amount = $this->roundAmount($amount);
        $formatted = number_format($amount, fmod($amount, 1.0) === 0.0 ? 0 : 2);

        return in_array($symbol, ['$', '€', '£'], true) ? $symbol.$formatted : trim($symbol.$formatted);
    }

    private function formatFrequencyLabel(string $frequency): string
    {
        return match ($frequency) {
            'weekly' => 'Weekly',
            'monthly' => 'Monthly',
            'daily' => 'Daily',
            'per_lesson' => 'Per lesson',
            default => ucfirst(str_replace('_', ' ', $frequency)),
        };
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function perLessonSuffix(array $data): string
    {
        $feeBasis = strtolower(trim((string) $this->extract($data, [
            'price_name',
            'class_details.price_name',
            'class_details.fee_basis',
            'fee_basis',
        ], 'per_lesson')));

        if (str_contains($feeBasis, 'month')) {
            return 'per month';
        }
        if (str_contains($feeBasis, 'week')) {
            return 'per week';
        }

        return 'per lesson';
    }

    private function formatInstallmentLabel(int $current, int $total): string
    {
        if ($current > 0 && $total > 1) {
            return "Payment {$current} of {$total}";
        }

        return '';
    }
}
