<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class CityPayoutService
{
    public const DEFAULT_PRIZE_PERCENTAGE = 70.00;

    private ?array $percentageByLocation = null;

    public function getPrizePercentage(?string $state, ?string $city): float
    {
        $state = $this->normalizeState($state);
        $city = $this->normalizeCity($city);

        if ($state === null || $city === null) {
            return self::DEFAULT_PRIZE_PERCENTAGE;
        }

        $map = $this->getPercentageMap();
        $key = $state . '|' . $city;

        return $map[$key] ?? self::DEFAULT_PRIZE_PERCENTAGE;
    }

    public function getHousePercentage(float $prizePercentage): float
    {
        $prizePercentage = $this->sanitizePercentage($prizePercentage);

        return round(100 - $prizePercentage, 2);
    }

    public function calculatePrizeAmount(float $grossAmount, ?string $state, ?string $city): float
    {
        $grossAmount = max(0, $grossAmount);
        $percentage = $this->getPrizePercentage($state, $city);

        return round($grossAmount * ($percentage / 100), 2);
    }

    public function calculateHouseAmount(float $grossAmount, ?string $state, ?string $city): float
    {
        $grossAmount = max(0, $grossAmount);
        $prizeAmount = $this->calculatePrizeAmount($grossAmount, $state, $city);

        return round($grossAmount - $prizeAmount, 2);
    }

    public function upsertSetting(string $state, string $city, float $prizePercentage): bool
    {
        if (! Schema::hasTable('city_payout_settings')) {
            return false;
        }

        $state = $this->normalizeState($state);
        $city = $this->normalizeCity($city);

        if ($state === null || $city === null) {
            return false;
        }

        $prizePercentage = $this->sanitizePercentage($prizePercentage);

        DB::table('city_payout_settings')->updateOrInsert(
            [
                'state' => $state,
                'city' => $city,
            ],
            [
                'prize_percentage' => $prizePercentage,
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        if ($this->percentageByLocation !== null) {
            $this->percentageByLocation[$state . '|' . $city] = $prizePercentage;
        }

        return true;
    }

    public function listSettings(int $limit = 500): Collection
    {
        if (! Schema::hasTable('city_payout_settings')) {
            return collect();
        }

        return DB::table('city_payout_settings')
            ->orderBy('state')
            ->orderBy('city')
            ->limit(max(1, $limit))
            ->get();
    }

    private function getPercentageMap(): array
    {
        if ($this->percentageByLocation !== null) {
            return $this->percentageByLocation;
        }

        if (! Schema::hasTable('city_payout_settings')) {
            $this->percentageByLocation = [];

            return $this->percentageByLocation;
        }

        $this->percentageByLocation = DB::table('city_payout_settings')
            ->get(['state', 'city', 'prize_percentage'])
            ->mapWithKeys(function ($setting): array {
                $state = $this->normalizeState($setting->state);
                $city = $this->normalizeCity($setting->city);

                if ($state === null || $city === null) {
                    return [];
                }

                return [
                    $state . '|' . $city => $this->sanitizePercentage((float) $setting->prize_percentage),
                ];
            })
            ->all();

        return $this->percentageByLocation;
    }

    private function normalizeState(?string $state): ?string
    {
        if ($state === null) {
            return null;
        }

        $clean = strtoupper(trim($state));

        return $clean === '' ? null : $clean;
    }

    private function normalizeCity(?string $city): ?string
    {
        if ($city === null) {
            return null;
        }

        $clean = preg_replace('/\s+/', ' ', trim($city)) ?? trim($city);

        return $clean === '' ? null : $clean;
    }

    private function sanitizePercentage(float $percentage): float
    {
        $percentage = max(0, min(100, $percentage));

        return round($percentage, 2);
    }
}
