<?php

namespace App\Services\GiftVouchers;

use App\Services\Paddle\Exceptions\PaddleException;
use App\Services\Paddle\PaddleClient;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class GiftVoucherCheckoutService
{
    public function __construct(private readonly PaddleClient $client) {}

    /**
     * @return array<int, array{key:string,label:string,amount:float,currency:string,paddle_price_id?:string|null,can_checkout:bool}>
     */
    public function tiers(): array
    {
        return collect(config('gift-vouchers.tiers', []))
            ->map(function (array $tier): array {
                $currency = Str::upper($tier['currency'] ?? 'EUR');
                $priceId = $tier['paddle_price_id'] ?? null;

                return [
                    'key' => $tier['key'],
                    'label' => $tier['label'],
                    'amount' => (float) $tier['amount'],
                    'currency' => $currency,
                    'paddle_price_id' => $priceId,
                    'can_checkout' => ! empty($priceId),
                ];
            })
            ->values()
            ->all();
    }

    /**
     * @param  array{tier_key:string,purchaser_email:string,recipient_email?:string|null,recipient_name?:string|null,message?:string|null,success_url?:string|null,return_url?:string|null}  $data
     * @return array{checkout_url:?string,expires_at:?string,id:?string}
     */
    public function create(array $data): array
    {
        $tier = $this->findTier($data['tier_key']);

        if (! $tier || empty($tier['paddle_price_id'])) {
            throw ValidationException::withMessages([
                'tier_key' => __('Gift voucher is not available right now.'),
            ]);
        }

        $customerId = $this->ensureCustomerId($data['purchaser_email']);

        $payload = [
            'items' => [
                [
                    'price_id' => $tier['paddle_price_id'],
                    'quantity' => 1,
                ],
            ],
            'customer_id' => $customerId,
            'custom_data' => array_filter([
                'type' => 'gift_voucher',
                'tier_key' => $tier['key'],
                'purchaser_email' => $data['purchaser_email'],
                'recipient_email' => $data['recipient_email'] ?? null,
                'recipient_name' => $data['recipient_name'] ?? null,
                'message' => $data['message'] ?? null,
                'app_locale' => App::getLocale(),
                'success_url' => $data['success_url'] ?? null,
                'cancel_url' => $data['return_url'] ?? null,
            ]),
        ];

        $response = $this->client->post('/transactions', $payload);

        return [
            'checkout_url' => Arr::get($response, 'data.checkout.url')
                ?? Arr::get($response, 'checkout.url')
                ?? Arr::get($response, 'data.url')
                ?? Arr::get($response, 'url'),
            'expires_at' => Arr::get($response, 'data.checkout.expires_at')
                ?? Arr::get($response, 'data.expires_at')
                ?? Arr::get($response, 'expires_at'),
            'id' => Arr::get($response, 'data.id') ?? Arr::get($response, 'id'),
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    protected function findTier(string $key): ?array
    {
        $tiers = collect(config('gift-vouchers.tiers', []))
            ->keyBy('key');

        $tier = $tiers->get($key);

        if (! $tier) {
            return null;
        }

        $tier['currency'] = Str::upper($tier['currency'] ?? 'EUR');

        return $tier;
    }

    protected function ensureCustomerId(string $email): string
    {
        $payload = ['email' => $email];

        try {
            $response = $this->client->post('/customers', $payload);
        } catch (PaddleException $exception) {
            $customerId = $this->resolveExistingCustomerId($email, $exception);

            if ($customerId) {
                return $customerId;
            }

            throw $exception;
        }

        $customerId = Arr::get($response, 'data.id') ?? Arr::get($response, 'id');

        if (! $customerId) {
            throw new PaddleException('Failed to create Paddle customer.');
        }

        return $customerId;
    }

    protected function resolveExistingCustomerId(string $email, PaddleException $exception): ?string
    {
        if ($exception->status() !== 409 || Arr::get($exception->context(), 'error.code') !== 'customer_already_exists') {
            return null;
        }

        $response = $this->client->get('/customers', [
            'email' => $email,
            'per_page' => 1,
        ]);

        return Arr::get($response, 'data.0.id') ?? Arr::get($response, 'data.0.customer_id');
    }
}
