<?php

namespace App\Services\Billing;

use Illuminate\Support\Facades\Http;

/**
 * Client for the platform Billing HTTP API — the one UserWallet lives on the
 * platform; CRM only consumes it. Amounts are integer minor units; the user is
 * identified by public_id. Authenticates with the per-consumer config('billing.api_key').
 */
class BillingClient
{
    private function base(): string
    {
        return rtrim((string) config('billing.api_url'), '/');
    }

    private function token(): string
    {
        return (string) (config('billing.api_key') ?? '');
    }

    public function balanceMinor(string $publicId): int
    {
        $res = Http::withToken($this->token())->acceptJson()->timeout(8)
            ->get($this->base().'/balance', ['user' => $publicId]);
        $res->throw();

        return (int) ($res->json('balance_minor') ?? 0);
    }

    public function canAfford(string $publicId, int $amountMinor): bool
    {
        $res = Http::withToken($this->token())->acceptJson()->timeout(8)
            ->get($this->base().'/can-afford', ['user' => $publicId, 'amount_minor' => $amountMinor]);
        $res->throw();

        return (bool) ($res->json('affordable') ?? false);
    }

    /**
     * Debit the wallet. Returns true on success, false on insufficient balance
     * (HTTP 402). Idempotent by $reference.
     */
    public function debit(string $publicId, int $amountMinor, string $source, string $reference, ?string $description = null): bool
    {
        $res = Http::withToken($this->token())->acceptJson()->timeout(10)->post($this->base().'/debit', array_filter([
            'user' => $publicId,
            'amount_minor' => $amountMinor,
            'service' => (string) config('billing.service', 'crm'),
            'source' => $source,
            'reference' => $reference,
            'description' => $description,
        ], static fn ($v) => $v !== null));

        if ($res->status() === 402) {
            return false;
        }
        $res->throw();

        return true;
    }

    /**
     * @param  array<string, mixed>  $metadata
     * @return array{checkout_url: string, reference: string}
     */
    public function initiatePlanCheckout(
        string $publicId,
        string $plan,
        int $months,
        int $amountMinor,
        string $returnUrl,
        array $metadata = [],
    ): array {
        $res = Http::withToken($this->token())->acceptJson()->timeout(15)->post($this->base().'/plan-checkout', [
            'user' => $publicId,
            'app' => (string) config('billing.service', 'care'),
            'plan' => $plan,
            'months' => $months,
            'amount_minor' => $amountMinor,
            'return_url' => $returnUrl,
            'metadata' => $metadata,
        ]);
        $res->throw();

        return [
            'checkout_url' => (string) $res->json('checkout_url'),
            'reference' => (string) $res->json('reference'),
        ];
    }

    /** @return array<string, mixed> */
    public function verifyPlanCheckout(string $reference): array
    {
        $res = Http::withToken($this->token())->acceptJson()->timeout(15)->post($this->base().'/plan-checkout/verify', [
            'reference' => $reference,
        ]);

        if ($res->status() === 422) {
            return ['paid' => false, 'error' => $res->json('error')];
        }
        $res->throw();

        return $res->json();
    }

    /**
     * Write suite messaging plan entitlement (SoT for included email/SMS allowances).
     *
     * @param  array{period_starts_at?: string, period_ends_at?: string|null, source?: string, metadata?: array}  $options
     */
    public function syncSuiteEntitlement(
        string $publicId,
        string $plan,
        string $status,
        string $reference,
        array $options = [],
    ): bool {
        if ($this->base() === '' || $this->token() === '') {
            return false;
        }

        $app = (string) config('billing.service', 'care');
        $starts = $options['period_starts_at'] ?? now()->toIso8601String();
        $ends = array_key_exists('period_ends_at', $options)
            ? $options['period_ends_at']
            : now()->addDays(30)->toIso8601String();

        try {
            $res = Http::withToken($this->token())
                ->acceptJson()
                ->timeout(15)
                ->post($this->base().'/suite-entitlements', array_filter([
                    'user' => $publicId,
                    'app' => $app,
                    'plan' => $plan,
                    'status' => $status,
                    'period_starts_at' => $starts,
                    'period_ends_at' => $ends,
                    'source' => $options['source'] ?? 'wallet_debit',
                    'reference' => $reference,
                    'metadata' => $options['metadata'] ?? null,
                ], static fn ($v) => $v !== null));

            return ! $res->failed();
        } catch (\Throwable) {
            return false;
        }
    }
}
