<?php

namespace App\Services\Providers;

use App\Models\Transaction;
use App\Services\Providers\Contracts\ProviderInterface;
use Illuminate\Support\Arr;

class MsorgProvider extends AbstractProvider implements ProviderInterface
{
    public const ENDPOINT_BALANCE = '/api/user/';
    public const ENDPOINT_DATA_PURCHASE = '/api/data/';
    public const ENDPOINT_DATA_QUERY = '/api/data/%s';
    public const ENDPOINT_AIRTIME_PURCHASE = '/api/topup/';
    public const ENDPOINT_AIRTIME_QUERY = '/api/topup/%s';
    public const ENDPOINT_EPIN_PURCHASE = '/api/epin/';
    public const ENDPOINT_ELECTRICITY_PURCHASE = '/api/billpayment/';
    public const ENDPOINT_ELECTRICITY_QUERY = '/api/billpayment/%s';
    public const ENDPOINT_VALIDATE_METER = '/api/validatemeter';
    public const ENDPOINT_CABLE_PURCHASE = '/api/cablesub/';
    public const ENDPOINT_CABLE_QUERY = '/api/cablesub/%s';
    public const ENDPOINT_VALIDATE_IUC = '/api/validateiuc';

    protected function headers(): array
    {
        return [
            'Authorization' => 'Token '.$this->vendor->secret,
        ];
    }

    public function getBalance(?Transaction $transaction = null): array
    {
        $result = $this->request('GET', self::ENDPOINT_BALANCE, [], $transaction);
        $httpCode = Arr::get($result, 'http_code');
        $httpOk = is_numeric($httpCode) && (int) $httpCode >= 200 && (int) $httpCode < 300;

        return [
            'success' => ! Arr::get($result, 'hard_failure', false) && $httpOk,
            'body' => Arr::get($result, 'body', []),
            'http_code' => $httpCode,
            'duration_ms' => Arr::get($result, 'duration_ms'),
        ];
    }

    public function getPlans(?Transaction $transaction = null): array
    {
        return [
            'success' => false,
            'body' => ['message' => 'MSORG plan sync endpoint not standardized'],
            'http_code' => null,
            'duration_ms' => null,
        ];
    }

    public function purchaseData(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        $payload = array_merge([
            'Ported_number' => true,
        ], $payload);

        return $this->normalizePurchaseResponse(
            $this->request('POST', self::ENDPOINT_DATA_PURCHASE, $payload, $transaction)
        );
    }

    public function purchaseAirtime(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        $payload = array_merge([
            'Ported_number' => true,
            'airtime_type' => 'VTU',
        ], $payload);

        return $this->normalizePurchaseResponse(
            $this->request('POST', self::ENDPOINT_AIRTIME_PURCHASE, $payload, $transaction)
        );
    }

    public function purchaseCable(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        return $this->normalizePurchaseResponse(
            $this->request('POST', self::ENDPOINT_CABLE_PURCHASE, $payload, $transaction)
        );
    }

    public function validateIuc(array $payload, ?Transaction $transaction = null): array
    {
        $result = $this->request('GET', self::ENDPOINT_VALIDATE_IUC, [
            'smart_card_number' => Arr::get($payload, 'smart_card_number'),
            'cablename' => Arr::get($payload, 'cablename'),
        ], $transaction);

        return $this->normalizeReadResult($result);
    }

    public function purchaseElectricity(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        return $this->normalizePurchaseResponse(
            $this->request('POST', self::ENDPOINT_ELECTRICITY_PURCHASE, $payload, $transaction)
        );
    }

    public function validateMeter(array $payload, ?Transaction $transaction = null): array
    {
        $result = $this->request('GET', self::ENDPOINT_VALIDATE_METER, [
            'meternumber' => Arr::get($payload, 'meternumber'),
            'disconame' => Arr::get($payload, 'disconame'),
            'mtype' => Arr::get($payload, 'mtype'),
        ], $transaction);

        return $this->normalizeReadResult($result);
    }

    public function purchaseEpin(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        return $this->normalizePurchaseResponse(
            $this->request('POST', self::ENDPOINT_EPIN_PURCHASE, $payload, $transaction)
        );
    }

    public function queryTransaction(array $payload, ?Transaction $transaction = null): ProviderResponse
    {
        $reference = (string) Arr::get($payload, 'reference');
        $service = (string) Arr::get($payload, 'service');

        $endpoint = $this->msorgQueryEndpointForService($service, $reference);

        if (! $endpoint) {
            return ProviderResponse::failed(
                message: 'Query endpoint is not available for this service',
                raw: ['service' => $service, 'reference' => $reference],
                hardFailure: false
            );
        }

        return $this->normalizePurchaseResponse(
            $this->request('GET', $endpoint, [], $transaction)
        );
    }

    protected function normalizeReadResult(array $result): array
    {
        $httpCode = Arr::get($result, 'http_code');
        $httpOk = is_numeric($httpCode) && (int) $httpCode >= 200 && (int) $httpCode < 300;

        return [
            'success' => ! Arr::get($result, 'hard_failure', false) && $httpOk,
            'body' => Arr::get($result, 'body', []),
            'http_code' => $httpCode,
            'duration_ms' => Arr::get($result, 'duration_ms'),
        ];
    }

    protected function msorgQueryEndpointForService(string $service, string $reference): ?string
    {
        return match ($service) {
            'data', 'data_sme', 'data_gifting', 'data_cg', 'data_direct' => sprintf(self::ENDPOINT_DATA_QUERY, $reference),
            'airtime', 'airtime_vtu' => sprintf(self::ENDPOINT_AIRTIME_QUERY, $reference),
            'cable' => sprintf(self::ENDPOINT_CABLE_QUERY, $reference),
            'electricity' => sprintf(self::ENDPOINT_ELECTRICITY_QUERY, $reference),
            default => null,
        };
    }
}
