<?php

namespace App\Services;

use App\Models\GtsOrder;
use App\Models\GtsProvider;
use App\Models\GtsService;
use Illuminate\Support\Facades\Http;
use Throwable;

class GtsProviderApi
{
    public function placeOrder(GtsOrder $order): void
    {
        $service = $order->service()->with('provider')->first();
        if (! $this->canAutoSubmit($service)) {
            return;
        }

        $response = $this->request($service->provider, [
            'action' => 'add',
            'service' => $service->provider_service_id,
            'link' => $order->target_link,
            'quantity' => $order->quantity,
        ]);

        $providerOrderId = data_get($response, 'order') ?: data_get($response, 'order_id');

        $order->forceFill([
            'provider_order_id' => $providerOrderId,
            'provider_status' => $providerOrderId ? 'submitted' : 'api_error',
            'provider_response' => $response,
            'api_submitted_at' => now(),
            'status' => $providerOrderId ? 'processing' : 'queued',
            'progress' => $providerOrderId ? max((int) $order->progress, 15) : $order->progress,
        ])->save();
    }

    public function syncOrder(GtsOrder $order): void
    {
        $service = $order->service()->with('provider')->first();
        if (! $service?->provider || ! $order->provider_order_id) {
            return;
        }

        $response = $this->request($service->provider, [
            'action' => 'status',
            'order' => $order->provider_order_id,
        ]);

        $providerStatus = strtolower((string) data_get($response, 'status', $order->provider_status));
        $remains = data_get($response, 'remains');
        $startCount = data_get($response, 'start_count');
        $status = match ($providerStatus) {
            'completed', 'complete' => 'completed',
            'partial' => 'partial',
            'canceled', 'cancelled', 'refunded' => 'rejected',
            default => 'processing',
        };

        $order->forceFill([
            'provider_status' => $providerStatus ?: $order->provider_status,
            'start_count' => is_numeric($startCount) ? (int) $startCount : $order->start_count,
            'remains' => is_numeric($remains) ? (int) $remains : $order->remains,
            'provider_response' => $response,
            'api_synced_at' => now(),
            'status' => $status,
            'progress' => $status === 'completed' ? 100 : max((int) $order->progress, $status === 'partial' ? 75 : 45),
        ])->save();
    }

    public function syncServices(GtsProvider $provider, float $profitPercent = 30): array
    {
        $result = $this->fetchServices($provider);
        $services = $result['services'];
        $this->importRemoteServices($provider, $services, [], $profitPercent);

        return $services;
    }

    public function fetchServices(GtsProvider $provider): array
    {
        $response = $this->request($provider, ['action' => 'services']);
        if (isset($response['error'])) {
            return ['services' => [], 'error' => $response['error']];
        }

        $services = data_get($response, 'services', $response);
        if (! is_array($services)) {
            return ['services' => [], 'error' => 'Provider services response is not a valid list.'];
        }

        $services = collect($services)
            ->filter(fn ($remote) => is_array($remote) && (string) data_get($remote, 'service', '') !== '')
            ->values()
            ->all();

        return ['services' => $services, 'error' => null];
    }

    public function importSelectedServices(GtsProvider $provider, array $serviceIds, float $bulkProfitPercent = 30, array $serviceProfits = []): int
    {
        $result = $this->fetchServices($provider);
        $wanted = collect($serviceIds)->map(fn ($id) => (string) $id)->filter()->unique()->values()->all();
        $services = collect($result['services'])
            ->filter(fn ($remote) => in_array((string) data_get($remote, 'service'), $wanted, true))
            ->values()
            ->all();

        return $this->importRemoteServices($provider, $services, $serviceProfits, $bulkProfitPercent);
    }

    private function importRemoteServices(GtsProvider $provider, array $services, array $serviceProfits = [], float $defaultProfitPercent = 30): int
    {
        $imported = 0;

        foreach ($services as $remote) {
            $remoteId = (string) data_get($remote, 'service');
            if ($remoteId === '') {
                continue;
            }

            $rate = (float) data_get($remote, 'rate', 0);
            $profit = array_key_exists($remoteId, $serviceProfits) && $serviceProfits[$remoteId] !== null && $serviceProfits[$remoteId] !== ''
                ? (float) $serviceProfits[$remoteId]
                : $defaultProfitPercent;
            $profit = max($profit, 0);

            GtsService::updateOrCreate([
                'gts_provider_id' => $provider->id,
                'provider_service_id' => $remoteId,
            ], [
                'category' => (string) data_get($remote, 'category', 'Imported'),
                'name' => (string) data_get($remote, 'name', 'Provider Service '.$remoteId),
                'description' => (string) data_get($remote, 'type', 'Imported from provider API.'),
                'min_quantity' => (int) data_get($remote, 'min', 1),
                'max_quantity' => (int) data_get($remote, 'max', 10000),
                'provider_rate_per_1000' => $rate,
                'profit_percent' => $profit,
                'rate_per_1000' => round($rate + (($rate * $profit) / 100), 4),
                'auto_submit' => true,
                'refill_enabled' => (bool) data_get($remote, 'refill', false),
                'dripfeed_enabled' => (bool) data_get($remote, 'dripfeed', false),
                'status' => 'active',
            ]);

            $imported++;
        }

        $provider->forceFill(['last_synced_at' => now()])->save();

        return $imported;
    }

    public function syncBalance(GtsProvider $provider): void
    {
        $response = $this->request($provider, ['action' => 'balance']);
        if (is_numeric(data_get($response, 'balance'))) {
            $provider->forceFill(['balance' => (float) data_get($response, 'balance'), 'last_synced_at' => now()])->save();
        }
    }

    private function canAutoSubmit(?GtsService $service): bool
    {
        return (bool) ($service?->auto_submit && $service->provider && $service->provider->status === 'active' && $service->provider_service_id);
    }

    private function request(GtsProvider $provider, array $payload): array
    {
        try {
            $response = Http::asForm()
                ->timeout(20)
                ->post($provider->api_url, ['key' => $provider->api_key] + $payload);
        } catch (Throwable $exception) {
            return ['error' => $exception->getMessage()];
        }

        if (! $response->ok()) {
            return ['error' => 'Provider returned HTTP '.$response->status(), 'body' => $response->body()];
        }

        return $response->json() ?: ['raw' => $response->body()];
    }
}
