<?php

namespace App\Services\Billing;

use App\Models\PricingPlan;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class PayPalPlanSyncService
{
    /**
     * @return array{created:int,updated:int,deactivated:int,skipped:int}
     */
    public function sync(string $mode = 'sandbox'): array
    {
        $result = [
            'created' => 0,
            'updated' => 0,
            'deactivated' => 0,
            'skipped' => 0,
        ];

        $token = $this->getAccessToken($mode);
        if (! $token) {
            Log::warning('PayPal plan sync aborted: missing access token', ['mode' => $mode]);

            return $result;
        }

        $productId = $this->ensureProduct($mode, $token);
        if (! $productId) {
            Log::warning('PayPal plan sync aborted: missing product id', ['mode' => $mode]);

            return $result;
        }

        $plans = PricingPlan::whereIn('code', ['personal_monthly', 'personal_annual'])->get();
        $keepIds = [];
        foreach ($plans as $plan) {
            $currentMeta = $plan->meta ?? [];
            $currentPlanId = $currentMeta['paypal'][$mode]['plan']['id'] ?? null;
            $history = $currentMeta['paypal'][$mode]['plan']['history'] ?? [];
            if ($currentPlanId) {
                $history = array_values(array_unique(array_merge([$currentPlanId], $history)));
            }

            $amountUsd = $this->toUsdAmount($plan->amount);
            $existing = $currentPlanId ? $this->getPlan($mode, $token, $currentPlanId) : null;
            $existingAmount = $existing['billing_cycles'][0]['pricing_scheme']['fixed_price']['value'] ?? null;

            if ($existing && $existingAmount !== null && (float) $existingAmount === (float) $amountUsd) {
                $currentMeta['paypal'][$mode]['plan'] = [
                    'id' => $currentPlanId,
                    'amount_usd' => $amountUsd,
                    'history' => $history,
                    'synced_at' => now()->toIso8601String(),
                ];
                $plan->update(['meta' => $currentMeta]);
                if ($currentPlanId) {
                    $keepIds[] = $currentPlanId;
                }
                $result['skipped']++;

                continue;
            }

            $newPlanId = $this->createPlan($mode, $token, $productId, $plan->code, $plan->name, $amountUsd, $plan->period);
            if (! $newPlanId) {
                $result['skipped']++;

                continue;
            }

            $currentMeta['paypal'][$mode]['plan'] = [
                'id' => $newPlanId,
                'amount_usd' => $amountUsd,
                'history' => $history,
                'synced_at' => now()->toIso8601String(),
            ];

            $plan->update(['meta' => $currentMeta]);
            if ($currentPlanId) {
                $result['updated']++;
            } else {
                $result['created']++;
            }
            $keepIds[] = $newPlanId;

            if ($this->canDeactivate($plan->code)) {
                foreach ($history as $oldId) {
                    if ($oldId === $newPlanId) {
                        continue;
                    }
                    if ($this->deactivatePlan($mode, $token, $oldId)) {
                        $result['deactivated']++;
                    }
                }
            }
        }

        // Deactivate any other active plans under this product to avoid confusion
        $deactivated = $this->deactivateOtherPlansForProduct($mode, $token, $productId, $keepIds);
        $result['deactivated'] += $deactivated;

        return $result;
    }

    private function toUsdAmount(int $idrAmount): string
    {
        $rate = (int) config('dewemoji.pricing.usd_rate', 15000);
        $usd = $rate > 0 ? $idrAmount / $rate : 0;

        return number_format(max($usd, 1), 2, '.', '');
    }

    private function canDeactivate(string $planCode): bool
    {
        $activeCount = \App\Models\Subscription::query()
            ->where('provider', 'paypal')
            ->where('status', 'active')
            ->where('plan', $planCode)
            ->count();

        return $activeCount === 0;
    }

    private function getAccessToken(string $mode): ?string
    {
        $clientId = config("dewemoji.billing.providers.paypal.{$mode}.client_id");
        $clientSecret = config("dewemoji.billing.providers.paypal.{$mode}.client_secret");
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");

        if (! $clientId || ! $clientSecret || ! $apiBase) {
            return null;
        }

        $res = Http::asForm()
            ->withBasicAuth($clientId, $clientSecret)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->post(rtrim($apiBase, '/').'/v1/oauth2/token', [
                'grant_type' => 'client_credentials',
            ]);

        if (! $res->successful()) {
            Log::warning('PayPal auth failed', ['body' => $res->body()]);

            return null;
        }

        return $res->json('access_token');
    }

    private function ensureProduct(string $mode, string $token): ?string
    {
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");
        $list = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->get(rtrim($apiBase, '/').'/v1/catalogs/products', [
                'page_size' => 20,
                'page' => 1,
            ]);

        if ($list->ok()) {
            $items = $list->json('products') ?? [];
            foreach ($items as $item) {
                if (($item['custom_id'] ?? '') === 'dewemoji-personal') {
                    return $item['id'] ?? null;
                }
            }
        }

        $payload = [
            'name' => 'Dewemoji Personal',
            'type' => 'SERVICE',
            'category' => 'SOFTWARE',
            'custom_id' => 'dewemoji-personal',
        ];

        $create = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->post(rtrim($apiBase, '/').'/v1/catalogs/products', $payload);
        $createdId = $create->json('id');
        if ($createdId) {
            if (! $create->successful()) {
                Log::warning('PayPal product create returned non-OK but provided id', [
                    'status' => $create->status(),
                    'body' => $create->body(),
                ]);
            }

            return $createdId;
        }

        Log::warning('PayPal product create failed', [
            'status' => $create->status(),
            'body' => $create->body(),
        ]);

        return null;
    }

    private function createPlan(
        string $mode,
        string $token,
        string $productId,
        string $code,
        string $name,
        string $amountUsd,
        ?string $period
    ): ?string {
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");
        $intervalUnit = $period === 'year' ? 'YEAR' : 'MONTH';

        $payload = [
            'product_id' => $productId,
            'name' => $name,
            'description' => "{$name} subscription",
            'billing_cycles' => [
                [
                    'frequency' => [
                        'interval_unit' => $intervalUnit,
                        'interval_count' => 1,
                    ],
                    'tenure_type' => 'REGULAR',
                    'sequence' => 1,
                    'total_cycles' => 0,
                    'pricing_scheme' => [
                        'fixed_price' => [
                            'value' => $amountUsd,
                            'currency_code' => 'USD',
                        ],
                    ],
                ],
            ],
            'payment_preferences' => [
                'auto_bill_outstanding' => true,
                'setup_fee_failure_action' => 'CONTINUE',
                'payment_failure_threshold' => 3,
            ],
        ];

        $res = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->post(rtrim($apiBase, '/').'/v1/billing/plans', $payload);

        $planId = $res->json('id');
        if ($planId) {
            if (! $res->successful()) {
                Log::warning('PayPal plan create returned non-OK but provided id', [
                    'code' => $code,
                    'status' => $res->status(),
                    'body' => $res->body(),
                ]);
            }

            return $planId;
        }

        Log::warning('PayPal plan create failed', [
            'code' => $code,
            'status' => $res->status(),
            'body' => $res->body(),
        ]);

        return null;
    }

    private function getPlan(string $mode, string $token, string $planId): ?array
    {
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");
        $res = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->get(rtrim($apiBase, '/').'/v1/billing/plans/'.$planId);

        if (! $res->successful()) {
            return null;
        }

        return $res->json();
    }

    private function deactivatePlan(string $mode, string $token, string $planId): bool
    {
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");
        $res = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->post(rtrim($apiBase, '/').'/v1/billing/plans/'.$planId.'/deactivate');

        return $res->successful();
    }

    private function deactivateOtherPlansForProduct(string $mode, string $token, string $productId, array $keepIds): int
    {
        $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base");
        $res = Http::withToken($token)
            ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10))
            ->get(rtrim($apiBase, '/').'/v1/billing/plans', [
                'product_id' => $productId,
                'page_size' => 20,
                'page' => 1,
            ]);

        if (! $res->successful()) {
            return 0;
        }

        $items = $res->json('plans') ?? [];
        if (! is_array($items)) {
            return 0;
        }

        $count = 0;
        foreach ($items as $plan) {
            $id = $plan['id'] ?? null;
            $status = strtoupper((string) ($plan['status'] ?? ''));
            if (! $id || in_array($id, $keepIds, true)) {
                continue;
            }
            if ($status !== 'ACTIVE') {
                continue;
            }
            if ($this->deactivatePlan($mode, $token, $id)) {
                $count++;
            }
        }

        return $count;
    }
}
