<?php

namespace App\Console\Commands;

use App\Models\Location;
use App\Services\LocationInventoryService;
use App\Services\LocationService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

/**
 * One-shot UAT / production multi-store DB cutover.
 *
 * Store-first: existing products.current_stock seeds into the default STORE
 * (Warangal), not the warehouse. Warehouse stays empty for future PO receipts.
 *
 * Example (UAT):
 *   php artisan inventory:multistore-cutover --migrate --force
 *
 * Dry-run first:
 *   php artisan inventory:multistore-cutover --migrate --dry-run
 */
class InventoryMultistoreCutover extends Command
{
    protected $signature = 'inventory:multistore-cutover
        {--migrate : Run pending database migrations (--force)}
        {--location= : Store location ID to backfill into (default: default store)}
        {--skip-backfill : Skip inventory balance backfill}
        {--skip-permissions : Skip multi-store permission sync}
        {--dry-run : Show the plan without writing data}
        {--force : Actually perform mutating steps (required unless dry-run)}';

    protected $description = 'UAT/prod multi-store cutover: migrate + ensure locations + permissions + store-first stock backfill';

    /** @var list<string> */
    protected array $multiStorePermissions = [
        'locations.view',
        'locations.manage',
        'inventory.balances.view',
        'transfers.view',
        'transfers.manage',
        'transfers.approve',
        'transfers.ship',
        'transfers.receive',
        'suppliers.manage',
        'purchase-orders.view',
        'purchase-orders.manage',
        'purchase-orders.receive',
        'allocation-rules.manage',
        'reports.location-ops',
    ];

    public function handle(LocationService $locations, LocationInventoryService $inventory): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $force = (bool) $this->option('force');

        if (!$dryRun && !$force) {
            $this->error('Refusing to run mutating cutover without --force (or use --dry-run).');
            $this->line('  php artisan inventory:multistore-cutover --migrate --dry-run');
            $this->line('  php artisan inventory:multistore-cutover --migrate --force');

            return self::FAILURE;
        }

        $this->info($dryRun ? 'Multi-store cutover (DRY-RUN)' : 'Multi-store cutover (LIVE)');
        $this->newLine();

        // 1) Migrations
        if ($this->option('migrate')) {
            $this->step('1) Database migrations');
            if ($dryRun) {
                $this->line('  Would run: php artisan migrate --force');
            } else {
                $code = Artisan::call('migrate', ['--force' => true]);
                $this->output->write(Artisan::output());
                if ($code !== 0) {
                    $this->error('Migrations failed.');

                    return self::FAILURE;
                }
                $this->info('  Migrations OK.');
            }
        } else {
            $this->step('1) Database migrations — skipped (pass --migrate to run)');
        }

        // 2) Ensure company + default store/warehouse rows
        $this->step('2) Ensure company + Central Warehouse + default Store');
        if ($dryRun) {
            $this->line('  Would call LocationService::ensureCompanyAndDefaults()');
        } else {
            $locations->ensureCompanyAndDefaults();
            $this->info('  Company / default locations ensured.');
        }

        if (!$inventory->useLocationInventory() && !$dryRun) {
            $this->error('inventory_balances / stock_ledger missing. Re-run with --migrate.');

            return self::FAILURE;
        }

        $store = Location::defaultStore();
        $warehouse = Location::centralWarehouse();
        $targetId = $this->option('location')
            ? (int) $this->option('location')
            : (int) ($store?->id ?? 0);

        if ($targetId <= 0 && !$dryRun) {
            $this->error('No target store location. Create default store or pass --location=ID.');

            return self::FAILURE;
        }

        $target = $targetId ? Location::query()->find($targetId) : null;
        $this->table(['Key', 'Value'], [
            ['Default store', $store ? "{$store->id} — {$store->name}" : 'MISSING'],
            ['Central warehouse', $warehouse ? "{$warehouse->id} — {$warehouse->name}" : 'MISSING'],
            ['Backfill target', $target ? "{$target->id} — {$target->name} ({$target->type})" : "ID {$targetId}"],
            ['Mode', $dryRun ? 'dry-run' : 'live'],
        ]);

        if ($target && $target->type === Location::TYPE_WAREHOUSE) {
            $this->warn('Target is a WAREHOUSE. Store-first cutover expects the live selling STORE.');
            if (!$dryRun && !$this->confirm('Backfill into warehouse anyway?', false)) {
                return self::FAILURE;
            }
        }

        // 3) Permissions
        if (!$this->option('skip-permissions')) {
            $this->step('3) Sync multi-store permissions → admin / super_admin');
            if ($dryRun) {
                $this->line('  Would create: ' . implode(', ', $this->multiStorePermissions));
                $this->line('  Would sync to roles: admin, super_admin');
            } else {
                $this->syncPermissions();
                $this->info('  Permissions synced.');
            }
        } else {
            $this->step('3) Permissions — skipped');
        }

        // 4) Pre-backfill stats
        $this->step('4) Pre-backfill inventory snapshot');
        $this->printBalanceSnapshot();

        $missingBefore = $this->countProductsMissingAnyBalance();
        $this->line("  Products with current_stock > 0 and no balance rows: {$missingBefore}");

        // 5) Store-first backfill
        if (!$this->option('skip-backfill')) {
            $this->step("5) Backfill products.current_stock → location #{$targetId} (store-first)");
            if ($dryRun) {
                $this->line("  Would run: inventory:backfill-locations --location={$targetId}");
                $this->line("  Would seed up to {$missingBefore} SKUs with no existing balances.");
            } else {
                $count = $inventory->backfillFromProductStock($targetId);
                $this->info("  Backfilled {$count} product balance rows into location #{$targetId}.");
            }
        } else {
            $this->step('5) Backfill — skipped');
        }

        // 6) Verify
        $this->step('6) Post-cutover check');
        if ($dryRun) {
            $this->line('  Would run: inventory:cutover-check');
        } else {
            Artisan::call('inventory:cutover-check');
            $this->output->write(Artisan::output());
        }

        $this->newLine();
        $this->info('Next (manual):');
        $this->line('  - Deploy app code to UAT if not already');
        $this->line('  - Assign staff default_location_id / store_user to Warangal store');
        $this->line('  - Smoke: POS sell, online in-stock, Admin Products stock, reports shop filter');
        $this->line('  - Do NOT re-run backfill on live data (double-count risk)');
        $this->line('  - Warehouse receives future stock via PO → transfer');

        return self::SUCCESS;
    }

    protected function step(string $title): void
    {
        $this->newLine();
        $this->warn($title);
    }

    protected function syncPermissions(): void
    {
        app()[PermissionRegistrar::class]->forgetCachedPermissions();

        foreach ($this->multiStorePermissions as $name) {
            Permission::firstOrCreate(
                ['name' => $name, 'guard_name' => 'web'],
                ['guard_name' => 'web']
            );
        }

        foreach (['admin', 'super_admin'] as $roleName) {
            $role = Role::firstOrCreate(['name' => $roleName, 'guard_name' => 'web']);
            $role->givePermissionTo($this->multiStorePermissions);
        }

        app()[PermissionRegistrar::class]->forgetCachedPermissions();
    }

    protected function printBalanceSnapshot(): void
    {
        if (!Schema::hasTable('inventory_balances')) {
            $this->line('  (inventory_balances table not present yet)');

            return;
        }

        $rows = DB::table('inventory_balances')
            ->select('location_id', DB::raw('COUNT(*) as row_count'), DB::raw('SUM(on_hand) as on_hand_sum'))
            ->groupBy('location_id')
            ->get();

        if ($rows->isEmpty()) {
            $this->line('  No inventory_balances rows yet.');

            return;
        }

        foreach ($rows as $row) {
            $loc = Location::query()->find($row->location_id);
            $label = $loc ? "{$loc->name} ({$loc->type})" : 'unknown';
            $this->line("  #{$row->location_id} {$label}: rows={$row->row_count} on_hand_sum={$row->on_hand_sum}");
        }
    }

    protected function countProductsMissingAnyBalance(): int
    {
        if (!Schema::hasTable('products') || !Schema::hasTable('inventory_balances')) {
            return 0;
        }

        return (int) (DB::selectOne("
            SELECT COUNT(*) AS c FROM products p
            WHERE p.current_stock > 0
              AND NOT EXISTS (SELECT 1 FROM inventory_balances b WHERE b.product_id = p.id)
        ")->c ?? 0);
    }
}
