<?php

namespace App\Services\Paddle;

use App\Enums\CouponType;
use App\Models\Coupon;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

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

    /**
     * @return array<string, mixed>
     */
    public function createDiscount(Coupon $coupon): array
    {
        $existing = $this->findExistingDiscount($coupon->code);
        if ($existing !== null) {
            return $existing;
        }

        $payload = $this->buildDiscountPayload($coupon);

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

        return Arr::get($response, 'data', $response);
    }

    /**
     * @return array<string, mixed>
     */
    public function updateDiscount(Coupon $coupon): array
    {
        if (! $coupon->paddle_discount_id) {
            return $this->createDiscount($coupon);
        }

        $payload = $this->buildDiscountPayload($coupon);

        $response = $this->client->patch('/discounts/'.$coupon->paddle_discount_id, $payload);

        return Arr::get($response, 'data', $response);
    }

    public function archiveDiscount(Coupon $coupon): void
    {
        if (! $coupon->paddle_discount_id) {
            return;
        }

        $this->client->delete('/discounts/'.$coupon->paddle_discount_id);
    }

    /**
     * @param  array<int, array{price_id: string, quantity?: int}>  $items
     * @param  array{currency?: string, address?: array{country_code: string, postal_code?: string}, customer_id?: string, address_id?: string}  $context
     * @return array<string, mixed>
     */
    public function previewDiscount(Coupon $coupon, array $items, array $context = []): array
    {
        $payload = [
            'items' => $items,
            'discount_id' => $coupon->paddle_discount_id,
        ];

        if (isset($context['currency'])) {
            $payload['currency_code'] = Str::upper($context['currency']);
        }

        if (isset($context['address'])) {
            $payload['address'] = $context['address'];
        }

        if (isset($context['customer_id'])) {
            $payload['customer_id'] = $context['customer_id'];
        }

        if (isset($context['address_id'])) {
            $payload['address_id'] = $context['address_id'];
        }

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

        return Arr::get($response, 'data', $response);
    }

    /**
     * @return array<string, mixed>|null
     */
    protected function findExistingDiscount(?string $code): ?array
    {
        $normalized = Str::upper(trim((string) $code));
        if ($normalized === '') {
            return null;
        }

        $response = $this->client->get('/discounts', [
            'code' => $normalized,
            'per_page' => 1,
        ]);

        $items = Arr::get($response, 'data', []);
        if (! is_array($items) || $items === []) {
            return null;
        }

        $match = Collection::make($items)->first(static function ($item) use ($normalized) {
            $codeValue = Str::upper((string) Arr::get($item, 'code', ''));

            return $codeValue === $normalized ? $item : null;
        });

        return is_array($match) ? $match : null;
    }

    /**
     * @return array<string, mixed>
     */
    protected function buildDiscountPayload(Coupon $coupon): array
    {
        $payload = [
            'code' => $coupon->code,
            'type' => $this->mapType($coupon->type),
            'amount' => $this->formatAmount($coupon),
            'currency_code' => Str::upper((string) ($coupon->currency ?? config('app.currency', 'EUR'))),
            'enabled_for_checkout' => $coupon->enabled_for_checkout,
            'description' => $this->resolveDescription($coupon),
            'mode' => $coupon->paddle_mode ?? 'standard',
            'usage_limit' => $coupon->usage_limit,
            'maximum_recurring_intervals' => null,
            'recur' => false,
            'restrict_to' => $this->resolveRestrictions($coupon),
            'expires_at' => optional($coupon->ends_at)?->toIso8601String(),
        ];

        if ($payload['type'] === 'percentage') {
            unset($payload['currency_code']);
        }

        return Collection::make($payload)
            ->reject(static fn ($value) => $value === null || $value === '')
            ->all();
    }

    protected function formatAmount(Coupon $coupon): string
    {
        if ($coupon->type === CouponType::PERCENTAGE) {
            return (string) $coupon->amount;
        }

        return (string) ((int) round($coupon->amount * 100));
    }

    protected function mapType(CouponType $type): string
    {
        return match ($type) {
            CouponType::PERCENTAGE => 'percentage',
            CouponType::FLAT => 'flat',
            CouponType::FLAT_PER_SEAT => 'flat_per_seat',
        };
    }

    protected function resolveRestrictions(Coupon $coupon): ?array
    {
        $packages = ($coupon->relationLoaded('packages')
            ? $coupon->packages
            : $coupon->packages()->get())
            ->whereNotNull('paddle_price_id');

        if ($packages->isEmpty()) {
            return null;
        }

        $prices = $packages->pluck('paddle_price_id')->filter()->values();

        return $prices->isEmpty() ? null : $prices->all();
    }

    protected function resolveDescription(Coupon $coupon): string
    {
        $description = trim((string) ($coupon->description ?? ''));

        if ($description !== '') {
            return $description;
        }

        $name = trim((string) ($coupon->name ?? ''));

        if ($name !== '') {
            return $name;
        }

        $code = trim((string) ($coupon->code ?? ''));

        if ($code !== '') {
            return sprintf('Coupon %s', $code);
        }

        return sprintf('Coupon %d', $coupon->id);
    }
}
