<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Plan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class ServerHunterService
{
    /**
     * Get all active VPS and dedicated plans as ServerHunter offers.
     *
     * @return array{version: int, offers: list<array<string, mixed>>}
     */
    public function buildFeed(): array
    {
        $plans = Plan::query()
            ->whereIn('service_type', ['vps', 'dedicated'])
            ->where('status', 'active')
            ->with('prices')
            ->orderBy('service_type')
            ->orderBy('sort_order')
            ->orderBy('price')
            ->get();

        $offers = $plans
            ->map(fn (Plan $plan): ?array => $this->transformPlan($plan))
            ->filter()
            ->values()
            ->all();

        return [
            'version' => 1,
            'offers' => $offers,
        ];
    }

    /**
     * Transform a Plan model into a ServerHunter offer array.
     *
     * @return array<string, mixed>|null
     */
    public function transformPlan(Plan $plan): ?array
    {
        if (! in_array($plan->service_type, ['vps', 'dedicated'], true)) {
            return null;
        }

        $config = $plan->provisioning_config ?? [];
        $specs = $this->resolveSpecs($plan, $config);
        $typeConfig = config("serverhunter.{$plan->service_type}", []);

        $monthlyPrice = $this->getMonthlyPrice($plan);
        if ($monthlyPrice === null) {
            return null;
        }

        $stock = $this->resolveStock($plan);

        return [
            'name' => $plan->name,
            'internal' => $plan->slug,
            'url' => 'https://ezscale.cloud/pricing',
            'currency' => config('serverhunter.currency'),
            'price' => number_format((float) $monthlyPrice, 2, '.', ''),
            'setup_fee' => $typeConfig['setup_fee'] ?? '0.00',
            'stock' => $stock,
            'billing_interval' => 'monthly',
            'product_type' => $plan->service_type,
            'virtualization' => $typeConfig['virtualization'] ?? 'kvm',
            'visibility' => 'visible',
            'gpu_name' => $specs['gpu_name'],
            'cpu_type' => $specs['cpu_type'],
            'cpu_name' => $specs['cpu_name'],
            'cpu_amount' => (string) $specs['cpu_amount'],
            'cpu_cores' => (string) $specs['cpu_cores'],
            'cpu_speed' => $specs['cpu_speed'],
            'memory_amount' => (string) $specs['memory_mb'],
            'memory_type' => $specs['memory_type'],
            'memory_ecc' => $specs['memory_ecc'],
            'hdd_amount' => (string) $specs['hdd_amount'],
            'hdd_capacity' => (string) $specs['hdd_capacity'],
            'ssd_amount' => (string) $specs['ssd_amount'],
            'ssd_capacity' => (string) $specs['ssd_capacity'],
            'uplink' => (string) $specs['uplink_mbps'],
            'traffic' => (string) $specs['bandwidth_tb'],
            'unmetered' => $typeConfig['unmetered'] ?? [],
            'operating_systems' => $typeConfig['operating_systems'] ?? [],
            'control_panel' => [],
            'country_code' => config('serverhunter.country_code'),
            'location' => config('serverhunter.location'),
            'coordinates' => config('serverhunter.coordinates'),
            'payment_methods' => config('serverhunter.payment_methods'),
            'features' => $typeConfig['features'] ?? [],
        ];
    }

    /**
     * Push offers to the ServerHunter API.
     *
     * @param  array{version: int, offers: list<array<string, mixed>>}  $feed
     */
    public function pushToApi(array $feed): array
    {
        $apiKey = config('serverhunter.api_key');

        if (empty($apiKey)) {
            throw new \RuntimeException('SERVERHUNTER_API_KEY is not configured.');
        }

        $response = Http::withToken($apiKey)
            ->timeout(30)
            ->post(config('serverhunter.api_url').'/v1/offers', $feed);

        return [
            'status' => $response->status(),
            'body' => $response->json(),
            'successful' => $response->successful(),
        ];
    }

    /**
     * Fetch and cache the ServerHunter spider IP whitelist.
     *
     * @return list<string>
     */
    public function getSpiderIps(): array
    {
        $ttl = (int) config('serverhunter.spider_ips_cache_ttl', 86400);

        return Cache::remember('serverhunter:spider-ips', $ttl, function (): array {
            $response = Http::timeout(10)
                ->get(config('serverhunter.spider_ips_url'));

            if (! $response->successful()) {
                return [];
            }

            $body = trim($response->body());

            if (empty($body)) {
                return [];
            }

            // The response is a newline-separated list of IPs
            return array_values(array_filter(
                array_map('trim', explode("\n", $body)),
                fn (string $ip): bool => $ip !== '' && filter_var($ip, FILTER_VALIDATE_IP) !== false,
            ));
        });
    }

    /**
     * Get the monthly price for a plan.
     */
    private function getMonthlyPrice(Plan $plan): ?string
    {
        // First try the plan_prices table for a monthly price
        $monthlyPrice = $plan->priceForCycle('monthly');

        if ($monthlyPrice !== null) {
            return $monthlyPrice->price;
        }

        // Fall back to the plan's base price
        if ($plan->price !== null && (float) $plan->price > 0) {
            return $plan->price;
        }

        return null;
    }

    /**
     * Resolve hardware specs from provisioning_config or derive from plan name.
     *
     * @param  array<string, mixed>  $config
     * @return array<string, mixed>
     */
    private function resolveSpecs(Plan $plan, array $config): array
    {
        $defaults = config('serverhunter.defaults', []);
        $inferredCores = $this->inferCoresFromSlug($plan->slug);

        $cpuCores = $config['cpu_cores'] ?? $inferredCores;
        $diskGb = $config['disk_gb'] ?? $this->inferDiskFromSlug($plan->slug, $cpuCores);
        $memoryMb = $config['memory_mb'] ?? ($cpuCores * 1024);
        $diskType = $config['disk_type'] ?? $defaults['disk_type'] ?? 'nvme';
        $isNvmeOrSsd = in_array($diskType, ['nvme', 'ssd'], true);

        return [
            'gpu_name' => $config['gpu_name'] ?? null,
            'cpu_type' => $config['cpu_type'] ?? $defaults['cpu_type'] ?? 'amd',
            'cpu_name' => $config['cpu_name'] ?? $defaults['cpu_name'] ?? 'EPYC',
            'cpu_speed' => $config['cpu_speed'] ?? $defaults['cpu_speed'] ?? '3.70',
            'cpu_amount' => $config['cpu_amount'] ?? 1,
            'cpu_cores' => $cpuCores,
            'memory_mb' => $memoryMb,
            'memory_type' => $config['memory_type'] ?? $defaults['memory_type'] ?? 'ddr4',
            'memory_ecc' => $config['memory_ecc'] ?? $defaults['memory_ecc'] ?? 'ecc',
            'hdd_amount' => $isNvmeOrSsd ? 0 : ($config['hdd_amount'] ?? 1),
            'hdd_capacity' => $isNvmeOrSsd ? 0 : ($config['hdd_capacity'] ?? $diskGb),
            'ssd_amount' => $isNvmeOrSsd ? ($config['ssd_amount'] ?? 1) : 0,
            'ssd_capacity' => $isNvmeOrSsd ? $diskGb : 0,
            'uplink_mbps' => $config['uplink_mbps'] ?? $defaults['uplink_mbps'] ?? 1000,
            'bandwidth_tb' => $config['bandwidth_tb'] ?? 0,
        ];
    }

    /**
     * Try to infer the number of CPU cores from the plan slug.
     * e.g., "vps-1" => 1, "vps-4" => 4. Storage plans default to 2 cores.
     */
    private function inferCoresFromSlug(string $slug): int
    {
        // Storage plans are not CPU-based — use sensible defaults
        if (str_starts_with($slug, 'stor-')) {
            return 2;
        }

        if (preg_match('/^vps-(\d+)$/', $slug, $matches)) {
            return max(1, (int) $matches[1]);
        }

        return 1;
    }

    /**
     * Infer disk size from slug. Storage plans use their capacity, VPS uses cores * 25.
     */
    private function inferDiskFromSlug(string $slug, int $cores): int
    {
        return match (true) {
            $slug === 'stor-500' => 500,
            $slug === 'stor-1tb' => 1000,
            str_starts_with($slug, 'stor-36bay') => 4000,
            default => $cores * 25,
        };
    }

    /**
     * Resolve stock status from plan attributes.
     */
    private function resolveStock(Plan $plan): string
    {
        if ($plan->stock_quantity === null) {
            return 'in_stock';
        }

        if ($plan->stock_quantity <= 0) {
            return 'out_of_stock';
        }

        if ($plan->stock_quantity <= 5) {
            return 'limited';
        }

        return 'in_stock';
    }
}
