<?php

namespace App\Console\Commands;

use App\Master\SiteManager;
use App\Repositories\SiteExportRepository;
use App\Services\SiteDownloadService;
use App\Tier;
use Illuminate\Console\Command;
use Mtc\MercuryDataModels\Tenant;

class DownloadSiteCommand extends Command
{
    protected $signature = 'download:site
        {--environment= : Environment to connect to (production/staging/custom)}
        {--url= : Custom URL for the environment}
        {--token= : Access token from Content Sync section}
        {--email= : User email associated with the token}
        {--tenant= : Remote tenant ID to download from}
        {--local-tenant= : Local tenant ID to import into (for update mode)}
        {--site-name= : Name for new site (for new mode)}
        {--full : Full sync (all modules)}
        {--modules=* : Modules for a partial sync (content, media, settings, dealerships, vehicles, offers, car-config, forms, customers)}
        {--new : Create new site (default is update existing)}';

    protected $description = 'Download a complete site from a remote environment to local';

    /**
     * Known environments mapped to their services.sync config URL keys.
     */
    private const ENVIRONMENTS = [
        'production' => 'services.sync.prod_url',
        'staging' => 'services.sync.vps_url',
    ];

    public function handle(SiteDownloadService $service, SiteManager $siteManager): int
    {

        // Get configuration from options or prompts
        $config = $this->getConfiguration();

        if (!$config) {
            return self::FAILURE;
        }

        // Configure the service
        $service->setConfig(
            $config['url'],
            $config['token'],
            $config['email'],
            $config['remote_tenant']
        );

        // Test connection
        $this->info('Testing connection...');
        $result = $service->testConnection();

        if (!$result['success']) {
            $this->error($result['message']);
            return self::FAILURE;
        }

        $this->info('Connection successful!');
        $this->newLine();

        // Get table list
        $this->info('Fetching table list...');

        try {
            $tables = $service->getTableList($config['modules']);
        } catch (\Exception $e) {
            $this->error('Failed to fetch table list: ' . $e->getMessage());
            return self::FAILURE;
        }

        if (empty($tables)) {
            $this->warn('No tables found to download.');
            return self::SUCCESS;
        }

        // Display tables to be downloaded
        $this->info('Tables to download:');
        $totalRecords = 0;
        foreach ($tables as $table) {
            $redacted = !empty($table['redacted']) ? ' [PII redacted]' : '';
            $this->line("  - {$table['name']} ({$table['count']} records){$redacted}");
            $totalRecords += $table['count'];
        }
        $this->newLine();
        $this->info("Total: " . count($tables) . " tables, {$totalRecords} records");
        $this->newLine();

        // Confirm before proceeding - only updates overwrite existing data,
        // new sites only lose their seed rows
        if (
            !$config['is_new']
            && !$this->confirm('This will TRUNCATE local tables before importing. Continue?', false)
        ) {
            $this->info('Operation cancelled.');
            return self::SUCCESS;
        }

        // Create or initialize tenant
        if ($config['is_new']) {
            $this->info("Creating new site: {$config['site_name']}...");
            $tenant = $siteManager->addSiteRecord($config['site_name'], '', Tier::ENTERPRISE->value);
            $this->info("Created tenant: {$tenant->id}");
        } else {
            $tenant = Tenant::find($config['local_tenant']);
            if (!$tenant) {
                $this->error("Local tenant not found: {$config['local_tenant']}");
                return self::FAILURE;
            }
        }

        // Initialize tenancy to switch to tenant database
        $this->info("Initializing tenancy for: {$tenant->name}...");
        tenancy()->initialize($tenant);
        $this->newLine();

        // Begin import process
        $this->info('Starting import...');
        $this->newLine();

        $importedTables = 0;
        $importedRecords = 0;
        $errors = [];

        foreach ($tables as $tableInfo) {
            $table = $tableInfo['name'];
            $recordCount = $tableInfo['count'];

            $this->info("Importing {$table}...");

            try {
                // Always truncate table first (new sites have seeded data)
                $service->truncateTable($table);

                if ($recordCount === 0) {
                    $this->line("  Skipped (empty table)");
                    continue;
                }

                // Calculate pages
                $perPage = 100;
                $totalPages = (int) ceil($recordCount / $perPage);

                // Create progress bar
                $progressBar = $this->output->createProgressBar($recordCount);
                $progressBar->start();

                $tableImported = 0;

                // Fetch and import each page
                for ($page = 1; $page <= $totalPages; $page++) {
                    $response = $service->fetchTableData($table, $page);

                    $records = $response['data'] ?? [];

                    if (!empty($records)) {
                        $imported = $service->importTable($table, $records);
                        $tableImported += $imported;
                        $progressBar->advance(count($records));
                    }
                }

                $progressBar->finish();
                $this->newLine();
                $this->line("  Imported {$tableImported} records");

                $importedTables++;
                $importedRecords += $tableImported;

            } catch (\Exception $e) {
                $this->newLine();
                $this->error("  Error: " . $e->getMessage());
                $errors[] = [
                    'table' => $table,
                    'error' => $e->getMessage(),
                ];
            }
        }

        // Summary
        $this->newLine();
        $this->info('==================');
        $this->info('Import Complete!');
        $this->info('==================');
        $this->info("Tables imported: {$importedTables}/" . count($tables));
        $this->info("Records imported: {$importedRecords}");

        if (!empty($errors)) {
            $this->newLine();
            $this->warn('Errors occurred:');
            foreach ($errors as $error) {
                $this->error("  - {$error['table']}: {$error['error']}");
            }
            return self::FAILURE;
        }

        return self::SUCCESS;
    }

    /**
     * Get configuration from options or interactive prompts
     */
    private function getConfiguration(): ?array
    {
        // Environment selection
        $env = $this->option('environment');
        $url = $this->option('url');

        if (!$env && !$url) {
            $env = $this->choice(
                'Which environment to connect to?',
                ['production', 'staging', 'custom'],
                0
            );
        }

        if ($env === 'custom' || (!$env && $url)) {
            $url = $url ?: $this->ask('Enter the custom endpoint URL');
            if (!$url) {
                $this->error('URL is required for custom environment');
                return null;
            }
        } else {
            $url = isset(self::ENVIRONMENTS[$env]) ? config(self::ENVIRONMENTS[$env]) : null;
            if (!$url) {
                $this->error("Unknown environment: {$env}");
                return null;
            }
        }

        // Token
        $token = $this->option('token') ?: $this->secret(
            'Enter the access token (from Content Sync section)'
        );

        if (!$token) {
            $this->error('Access token is required');
            return null;
        }

        // Email
        $email = $this->option('email') ?: $this->ask(
            'Enter your email address (associated with the token)'
        );

        if (!$email) {
            $this->error('Email is required');
            return null;
        }

        // Remote Tenant ID (to download from)
        $remoteTenant = $this->option('tenant') ?: $this->ask(
            'Enter the remote tenant ID to download from'
        );

        if (!$remoteTenant) {
            $this->error('Remote tenant ID is required');
            return null;
        }

        // New or update
        $isNew = $this->option('new');
        if (!$isNew && !$this->option('no-interaction')) {
            $mode = $this->choice(
                'Create new site or update existing?',
                ['update', 'new'],
                0
            );
            $isNew = $mode === 'new';
        }

        // Get local tenant or site name based on mode
        $localTenant = null;
        $siteName = null;

        if ($isNew) {
            $siteName = $this->option('site-name') ?: $this->ask(
                'Enter the name for the new site'
            );
            if (!$siteName) {
                $this->error('Site name is required for new sites');
                return null;
            }
        } else {
            $localTenant = $this->option('local-tenant') ?: $this->ask(
                'Enter the local tenant ID to import into'
            );
            if (!$localTenant) {
                $this->error('Local tenant ID is required for updates');
                return null;
            }
        }

        // Full or partial sync
        $modules = $this->getModuleSelection();

        if ($modules === null) {
            return null;
        }

        // Show configuration summary
        $this->newLine();
        $this->info('Configuration:');
        $this->line("  Environment: {$url}");
        $this->line("  Remote Tenant: {$remoteTenant}");
        if ($isNew) {
            $this->line("  Mode: Create new site '{$siteName}'");
        } else {
            $this->line("  Mode: Update existing tenant '{$localTenant}'");
        }
        $this->line("  Sync: " . (empty($modules) ? 'full (all modules)' : 'partial (' . implode(', ', $modules) . ')'));
        $this->newLine();

        if (!$this->confirm('Proceed with this configuration?', true)) {
            return null;
        }

        return [
            'url' => $url,
            'token' => $token,
            'email' => $email,
            'remote_tenant' => $remoteTenant,
            'local_tenant' => $localTenant,
            'site_name' => $siteName,
            'is_new' => $isNew,
            'modules' => $modules,
        ];
    }

    /**
     * Resolve the module selection for the sync. Returns [] for a full sync,
     * a list of module keys for a partial sync, or null when input is invalid.
     *
     * @return string[]|null
     */
    private function getModuleSelection(): ?array
    {
        $available = array_keys(SiteExportRepository::MODULES);
        $modules = array_values(array_filter((array) $this->option('modules')));

        $unknown = array_diff($modules, $available);
        if (!empty($unknown)) {
            $this->error('Unknown modules: ' . implode(', ', $unknown));
            $this->line('Available modules: ' . implode(', ', $available));
            return null;
        }

        if ($this->option('full') || !empty($modules) || $this->option('no-interaction')) {
            return $this->option('full') ? [] : $modules;
        }

        $mode = $this->choice('Full or partial sync?', ['full', 'partial'], 0);

        if ($mode === 'full') {
            return [];
        }

        $choices = [];
        foreach (SiteExportRepository::MODULES as $key => $module) {
            $choices[$key] = $module['label'];
        }

        $this->newLine();
        $this->info('Available modules:');
        foreach ($choices as $key => $label) {
            $this->line("  {$key} - {$label}");
        }

        $selected = $this->choice(
            'Which modules to sync? (comma-separated for multiple)',
            array_keys($choices),
            null,
            null,
            true
        );

        return array_values(array_unique((array) $selected));
    }
}
