<?php

namespace App\Console\Commands;

use App\Models\PaymentProvider;
use App\Models\Plan;
use App\Services\Billing\BillingManager;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use ReflectionClass;
use RuntimeException;

/**
 * Spin up a PayPal Catalog Product + Subscription Plan from the
 * terminal. Saves admin from the two-step curl dance documented in
 * paypal-integration.md (sandbox UI is hit-or-miss for Plan creation).
 *
 * Resolves credentials + mode from the configured PaypalProvider so
 * test/live + the right OAuth token come along for free. Prints the
 * resulting Plan ID — admin pastes it into /admin/billing → Plans →
 * {plan} → PayPal Plan ID field.
 *
 * Usage (interactive — prompts for missing values):
 *   php artisan paypal:create-plan
 *
 * Usage (scripted):
 *   php artisan paypal:create-plan \
 *     --plan=pro --period=monthly --price=7.50 --currency=USD \
 *     --product-name="AppShot AI Pro"
 */
class PaypalCreatePlanCommand extends Command
{
    protected $signature = 'paypal:create-plan
                            {--plan= : Local plan key (e.g. pro). Used for naming + audit only — no DB write.}
                            {--period=monthly : monthly|yearly}
                            {--price= : Decimal amount (e.g. 7.50)}
                            {--currency=USD : ISO currency code (USD/INR/EUR/GBP/...)}
                            {--product-name= : PayPal Product display name (e.g. "AppShot AI Pro")}
                            {--product-id= : Existing PayPal Product ID (PROD-XXX) to reuse — skips Product create}
                            {--mode= : Override active provider mode (test|live). Defaults to the configured mode.}
                            {--tax-percentage=0 : Tax % applied to every charge (e.g. 18 for GST). 0 = no tax.}
                            {--tax-inclusive=false : true|false — true means the --price already includes tax.}';

    protected $description = 'Create a PayPal Catalog Product + Subscription Plan via the PayPal API.';

    public function handle(BillingManager $manager): int
    {
        // Resolve provider + access token via reflection — reuses our
        // existing OAuth caching + sandbox/live URL switching without
        // duplicating credential lookup logic.
        $provider = $manager->provider('paypal');
        if (! $provider) {
            $this->error('PayPal provider not registered.');

            return self::FAILURE;
        }

        $row = PaymentProvider::query()->where('key', 'paypal')->first();
        if (! $row) {
            $this->error('No payment_providers row for paypal. Save credentials at /admin/billing first.');

            return self::FAILURE;
        }

        $modeOverride = $this->option('mode');
        if ($modeOverride && ! in_array($modeOverride, ['test', 'live'], true)) {
            $this->error("Invalid --mode '{$modeOverride}' (must be test or live).");

            return self::FAILURE;
        }

        // Use reflection to call the private mode() + apiBase() + accessToken()
        // helpers on PaypalProvider. The alternative (making them public)
        // would widen the provider's API surface for a one-off CLI helper —
        // not worth it.
        $ref = new ReflectionClass($provider);
        $currentMode = $this->callPrivate($ref, $provider, 'mode');
        $mode = $modeOverride ?: $currentMode;

        if ($mode !== $currentMode) {
            // Caller asked for a different mode than the row's active
            // mode — temporarily flip + restore. Required because
            // accessToken() / apiBase() both branch on mode().
            $config = $row->config ?? [];
            $config['mode'] = $mode;
            $row->update(['config' => $config]);
        }

        $apiBase = $this->callPrivate($ref, $provider, 'apiBase');
        $this->line("Mode: <fg=cyan>{$mode}</> · API base: <fg=cyan>{$apiBase}</>");

        try {
            $accessToken = $this->callPrivate($ref, $provider, 'accessToken');
        } catch (\Throwable $e) {
            $this->error("Could not get PayPal access token: {$e->getMessage()}");
            $this->error("Check {$mode}-mode credentials at /admin/billing → PayPal panel.");

            return self::FAILURE;
        }

        // -- Collect / prompt for inputs ----------------------------------

        $planKey = $this->option('plan') ?: $this->ask('Local plan key (just for naming; e.g. pro)', 'pro');
        $localPlan = Plan::query()->where('key', $planKey)->first();
        if (! $localPlan) {
            $this->warn("No local plan with key '{$planKey}' — proceeding anyway (PayPal plan creation only).");
        }

        $period = $this->option('period');
        if (! in_array($period, ['monthly', 'yearly'], true)) {
            $period = $this->choice('Period', ['monthly', 'yearly'], 'monthly');
        }

        $price = $this->option('price') ?: $this->ask('Price (decimal, e.g. 7.50)');
        if (! is_numeric($price) || (float) $price <= 0) {
            $this->error("Invalid price '{$price}'.");

            return self::FAILURE;
        }
        $price = number_format((float) $price, 2, '.', '');

        $currency = strtoupper($this->option('currency') ?: 'USD');
        if (! preg_match('/^[A-Z]{3}$/', $currency)) {
            $this->error("Invalid currency '{$currency}' (must be 3-letter ISO code).");

            return self::FAILURE;
        }

        $productId = $this->option('product-id');
        $productName = $this->option('product-name');

        // -- Step 1: Product (skip if --product-id passed) ----------------

        if (! $productId) {
            if (! $productName) {
                // Pull the admin-editable brand name so a renamed install
                // (e.g. "Acme Shots") gets its PayPal product list named
                // accordingly. Falls back to APP_NAME env when no setting
                // exists yet.
                $brand = (string) \App\Services\Settings::get('site.name', (string) config('app.name', 'AppShot AI'));
                $defaultName = $localPlan
                    ? "{$brand} {$localPlan->name}"
                    : $brand;
                $productName = $this->ask('Product name', $defaultName);
            }

            $this->line("\nCreating Product <fg=cyan>{$productName}</>…");
            $productResponse = Http::withToken($accessToken)
                ->acceptJson()
                ->asJson()
                ->timeout(20)
                ->post($apiBase.'/v1/catalogs/products', [
                    'name' => $productName,
                    'type' => 'SERVICE',
                    'category' => 'SOFTWARE',
                ]);

            if (! $productResponse->successful()) {
                $this->error("Product create failed [{$productResponse->status()}]:");
                $this->line($productResponse->body());

                return self::FAILURE;
            }

            $productId = (string) $productResponse->json('id');
            $this->info("Product created: {$productId}");
        } else {
            $this->line("Reusing existing Product: <fg=cyan>{$productId}</>");
        }

        // -- Step 2: Plan -------------------------------------------------

        $planName = $localPlan
            ? "{$localPlan->name} ({$period})"
            : "{$planKey} ({$period})";

        $this->line("\nCreating Plan <fg=cyan>{$planName}</> at <fg=cyan>{$price} {$currency}</> / {$period}…");

        $planResponse = Http::withToken($accessToken)
            ->acceptJson()
            ->asJson()
            ->timeout(20)
            ->post($apiBase.'/v1/billing/plans', [
                'product_id' => $productId,
                'name' => $planName,
                'description' => "Auto-created via artisan paypal:create-plan",
                'status' => 'ACTIVE',
                'billing_cycles' => [
                    [
                        'frequency' => [
                            'interval_unit' => $period === 'yearly' ? 'YEAR' : 'MONTH',
                            'interval_count' => 1,
                        ],
                        'tenure_type' => 'REGULAR',
                        'sequence' => 1,
                        'total_cycles' => 0, // 0 = infinite recurring
                        'pricing_scheme' => [
                            'fixed_price' => [
                                'value' => $price,
                                'currency_code' => $currency,
                            ],
                        ],
                    ],
                ],
                'payment_preferences' => [
                    'auto_bill_outstanding' => true,
                    'setup_fee' => ['value' => '0', 'currency_code' => $currency],
                    'setup_fee_failure_action' => 'CONTINUE',
                    'payment_failure_threshold' => 3,
                ],
                // Tax block is technically optional, but PayPal's hosted
                // approval page's installment-plan renderer reads it and
                // crashes if absent for certain currency+locale combos.
                // Setting 0% non-inclusive is the safe minimum that
                // suppresses the broken installment widget. Admin may
                // override --tax-percentage > 0 to collect tax on every
                // charge (PayPal echoes amount.breakdown.tax_total on
                // each payment webhook, which we surface on invoices).
                'taxes' => [
                    'percentage' => (string) $this->option('tax-percentage'),
                    'inclusive' => filter_var($this->option('tax-inclusive'), FILTER_VALIDATE_BOOLEAN),
                ],
            ]);

        if (! $planResponse->successful()) {
            $this->error("Plan create failed [{$planResponse->status()}]:");
            $this->line($planResponse->body());

            return self::FAILURE;
        }

        $planId = (string) $planResponse->json('id');

        $this->newLine();
        $this->info("✓ PayPal Plan created");
        $this->newLine();
        $this->line("  <fg=yellow>Product ID:</> {$productId}");
        $this->line("  <fg=yellow>Plan ID:</>    {$planId}");
        $this->line("  <fg=yellow>Mode:</>       {$mode}");
        $this->line("  <fg=yellow>Price:</>      {$price} {$currency} / {$period}");
        $this->newLine();
        $this->line("Next step → paste the Plan ID into <fg=cyan>/admin/billing</> → Plans → "
            .($localPlan?->name ?? $planKey)." → PayPal Plan ID ({$period}).");

        return self::SUCCESS;
    }

    /** Reflection helper — calls a private/protected method on the provider. */
    private function callPrivate(ReflectionClass $ref, object $instance, string $method): mixed
    {
        $m = $ref->getMethod($method);
        $m->setAccessible(true);

        return $m->invoke($instance);
    }
}
