<?php

declare(strict_types=1);

namespace Kylin\Payment\Services;

use Illuminate\Contracts\Container\Container;

final class CouponService
{
    public const string TYPE_DISCOUNT = 'discount';
    public const string TYPE_FIXED = 'fixed';
    public const string TYPE_RANDOM = 'random';
    public const string TYPE_FREE_SHIPPING = 'free_shipping';

    public function __construct(
        protected readonly Container $app
    ) {
    }

    public function validate(array $coupon, float $orderAmount): bool
    {
        if (empty($coupon)) {
            return false;
        }

        if (isset($coupon['expire_at']) && $coupon['expire_at'] < time()) {
            return false;
        }

        if (isset($coupon['min_amount']) && $orderAmount < $coupon['min_amount']) {
            return false;
        }

        if (isset($coupon['max_uses']) && $coupon['max_uses'] <= 0) {
            return false;
        }

        return true;
    }

    public function calculateDiscount(array $coupon, float $orderAmount): float
    {
        if (!$this->validate($coupon, $orderAmount)) {
            return 0.0;
        }

        $type = $coupon['type'] ?? self::TYPE_DISCOUNT;

        $discount = match ($type) {
            self::TYPE_DISCOUNT => $orderAmount * ((100 - ($coupon['value'] ?? 100)) / 100),
            self::TYPE_FIXED => min($coupon['value'] ?? 0, $orderAmount),
            self::TYPE_RANDOM => $this->calculateRandomDiscount($coupon, $orderAmount),
            self::TYPE_FREE_SHIPPING => $coupon['shipping_fee'] ?? 0,
            default => 0.0,
        };

        $discount = max(0, $discount);
        $discount = min($discount, $orderAmount);

        return round($discount, 2);
    }

    private function calculateRandomDiscount(array $coupon, float $orderAmount): float
    {
        $min = $coupon['min_value'] ?? 0;
        $max = $coupon['max_value'] ?? 0;
        return min(rand((int)($min * 100), (int)($max * 100)) / 100, $orderAmount);
    }

    public function apply(array $coupon, float $orderAmount): array
    {
        $discountAmount = $this->calculateDiscount($coupon, $orderAmount);
        $actualAmount = round($orderAmount - $discountAmount, 2);

        return [
            'original_amount' => round($orderAmount, 2),
            'discount_amount' => $discountAmount,
            'actual_amount' => $actualAmount,
            'coupon' => $coupon,
        ];
    }

    public function createDiscountCoupon(array $params): array
    {
        $defaultParams = [
            'code' => $this->generateCouponCode(),
            'name' => '折扣券',
            'type' => self::TYPE_DISCOUNT,
            'value' => 90,
            'min_amount' => 0,
            'max_discount' => 0,
            'expire_at' => time() + 30 * 24 * 3600,
            'max_uses' => 1,
            'description' => '',
        ];

        return array_merge($defaultParams, $params);
    }

    public function createFixedCoupon(array $params): array
    {
        $defaultParams = [
            'code' => $this->generateCouponCode(),
            'name' => '满减券',
            'type' => self::TYPE_FIXED,
            'value' => 10,
            'min_amount' => 100,
            'expire_at' => time() + 30 * 24 * 3600,
            'max_uses' => 1,
            'description' => '',
        ];

        return array_merge($defaultParams, $params);
    }

    public function createRandomCoupon(array $params): array
    {
        $defaultParams = [
            'code' => $this->generateCouponCode(),
            'name' => '随机金额券',
            'type' => self::TYPE_RANDOM,
            'min_value' => 5,
            'max_value' => 50,
            'min_amount' => 0,
            'expire_at' => time() + 30 * 24 * 3600,
            'max_uses' => 1,
            'description' => '',
        ];

        return array_merge($defaultParams, $params);
    }

    public function createFreeShippingCoupon(array $params): array
    {
        $defaultParams = [
            'code' => $this->generateCouponCode(),
            'name' => '免运费券',
            'type' => self::TYPE_FREE_SHIPPING,
            'shipping_fee' => 10,
            'min_amount' => 0,
            'expire_at' => time() + 30 * 24 * 3600,
            'max_uses' => 1,
            'description' => '',
        ];

        return array_merge($defaultParams, $params);
    }

    public function generateCouponCode(int $length = 12): string
    {
        $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $code = '';

        for ($i = 0; $i < $length; $i++) {
            $code .= $characters[rand(0, strlen($characters) - 1)];
        }

        return $code;
    }

    public function getCouponByCode(string $code, array $coupons): ?array
    {
        foreach ($coupons as $coupon) {
            if ($coupon['code'] === $code) {
                return $coupon;
            }
        }

        return null;
    }

    public function updateCouponUses(array $coupon): array
    {
        if (isset($coupon['max_uses'])) {
            $coupon['max_uses']--;
        }

        return $coupon;
    }

    public function batchCreateCoupons(string $type, int $count, array $params = []): array
    {
        $coupons = [];

        for ($i = 0; $i < $count; $i++) {
            $coupon = match ($type) {
                self::TYPE_DISCOUNT => $this->createDiscountCoupon($params),
                self::TYPE_FIXED => $this->createFixedCoupon($params),
                self::TYPE_RANDOM => $this->createRandomCoupon($params),
                self::TYPE_FREE_SHIPPING => $this->createFreeShippingCoupon($params),
                default => null,
            };

            if ($coupon) {
                $coupons[] = $coupon;
            }
        }

        return $coupons;
    }
}
