<?php

namespace App\Console\Commands;

use App\Models\GmailSale;
use App\Models\GmailSellSetting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;

class DeployCheck extends Command
{
    protected $signature = 'app:deploy-check';

    protected $description = 'Show shared-hosting deploy diagnostics and Gmail accounting readiness';

    private int $failures = 0;

    private int $warnings = 0;

    public function handle(): int
    {
        $this->line('Shared Hosting Deploy Check');
        $this->newLine();

        $this->section('Runtime');
        $this->status(
            version_compare(PHP_VERSION, '8.2.0', '>='),
            'PHP version',
            PHP_VERSION,
            'Laravel 12 should run on PHP 8.2 or newer.'
        );
        $this->infoLine('Laravel version', app()->version());
        $this->warnUnless(config('app.env') === 'production', 'APP_ENV', (string) config('app.env'), 'Live hosting should usually use production.');
        $this->warnUnless(config('app.debug') === false, 'APP_DEBUG', config('app.debug') ? 'true' : 'false', 'Keep APP_DEBUG=false on live hosting.');
        $this->warnUnless(
            !str_contains((string) config('app.url'), 'localhost') && !str_contains((string) config('app.url'), '127.0.0.1'),
            'APP_URL',
            (string) config('app.url'),
            'APP_URL still looks local.'
        );
        $this->infoLine('Timezone', (string) config('app.timezone'));

        $this->section('Cache');
        $this->warnUnless(!$this->hasConfigCache(), 'Config cache', $this->hasConfigCache() ? 'present' : 'clear', 'After upload, run php artisan optimize:clear before migrate.');
        $this->warnUnless(!$this->hasRouteCache(), 'Route cache', $this->hasRouteCache() ? 'present' : 'clear', 'Clear route cache if routes changed.');
        $this->infoLine('View cache path', (string) storage_path('framework/views'));

        $this->section('Database');
        $this->infoLine('Connection', (string) config('database.default'));
        $this->infoLine('Database name', $this->databaseName());
        $this->status($this->databaseConnectionWorks(), 'Database ping', $this->databaseVersion(), 'Database connection failed.');

        $this->section('Gmail schema');
        $this->schemaCheck('gmail_sales', null);
        $this->schemaCheck('gmail_sales', 'rate');
        $this->schemaCheck('gmail_sales', 'admin_price');
        $this->schemaCheck('gmail_sales', 'upline_commissions_snapshot');
        $this->schemaCheck('gmail_sell_settings', null);
        $this->schemaCheck('gmail_sell_settings', 'price');
        $this->schemaCheck('gmail_sell_settings', 'admin_price');
        $this->schemaCheck('gmail_sell_settings', 'stats_reset_at');
        $this->schemaCheck('gmail_sell_settings', 'upline_commissions');
        $this->schemaCheck('balance_logs', null, false);
        $this->schemaCheck('balance_logs', 'type', false);
        $this->schemaCheck('income_histories', null, false);

        $this->section('Gmail data');
        $this->gmailSettingsStatus();
        $this->gmailSalesStatus();

        $this->section('Filesystem and jobs');
        $this->warnUnless(is_link(public_path('storage')) || File::exists(public_path('storage')), 'Storage link', File::exists(public_path('storage')) ? 'available' : 'missing', 'Run php artisan storage:link if user files are missing.');
        $this->infoLine('Queue driver', (string) config('queue.default'));
        $this->warnUnless(
            (string) config('queue.default') === 'sync',
            'Queue worker',
            (string) config('queue.default'),
            'If queue is not sync, keep a worker running on the server.'
        );
        $this->warnLine('Cron', 'required', 'For scheduled jobs run php artisan schedule:run every minute.');

        $this->newLine();
        if ($this->failures > 0) {
            $this->error("Critical problems: {$this->failures}");
        } else {
            $this->info('Critical problems: 0');
        }

        if ($this->warnings > 0) {
            $this->warn("Warnings: {$this->warnings}");
        } else {
            $this->line('[OK] Warnings: 0');
        }

        $this->newLine();
        $this->line('Recommended deploy order:');
        $this->line('1. php artisan optimize:clear');
        $this->line('2. php artisan migrate --force');
        $this->line('3. php artisan storage:link');
        $this->line('4. php artisan app:deploy-check');
        $this->line('5. If everything is OK, cache again if needed.');

        return self::SUCCESS;
    }

    private function section(string $title): void
    {
        $this->newLine();
        $this->line($title);
    }

    private function schemaCheck(string $table, ?string $column = null, bool $required = true): void
    {
        $label = $column ? "{$table}.{$column}" : $table;

        try {
            $ok = $column
                ? Schema::hasTable($table) && Schema::hasColumn($table, $column)
                : Schema::hasTable($table);
        } catch (\Throwable $e) {
            $this->status(false, $label, 'unreachable', $e->getMessage());
            return;
        }

        if ($required) {
            $this->status($ok, $label, $ok ? 'present' : 'missing', 'Run migrations on the hosting database.');
            return;
        }

        $this->warnUnless($ok, $label, $ok ? 'present' : 'missing', 'Legacy fallback will be limited if this is missing.');
    }

    private function gmailSettingsStatus(): void
    {
        try {
            if (!Schema::hasTable('gmail_sell_settings')) {
                $this->status(false, 'Gmail setting row', 'unavailable', 'gmail_sell_settings table is missing.');
                return;
            }

            $setting = GmailSellSetting::query()->first();
            if (!$setting) {
                $this->warnLine('Gmail setting row', 'missing', 'Create and save Gmail settings once on the server.');
                return;
            }

            $this->infoLine('Gmail per account rate', number_format((float) ($setting->price ?? 0), 2));
            $this->infoLine('Gmail admin price', number_format((float) ($setting->admin_price ?? 0), 2));
            $this->infoLine('Gmail stats reset at', $setting->stats_reset_at ? $setting->stats_reset_at->timezone('Asia/Dhaka')->format('Y-m-d H:i:s') : 'not set');
        } catch (\Throwable $e) {
            $this->status(false, 'Gmail setting row', 'error', $e->getMessage());
        }
    }

    private function gmailSalesStatus(): void
    {
        try {
            if (!Schema::hasTable('gmail_sales')) {
                $this->status(false, 'gmail_sales rows', 'unavailable', 'gmail_sales table is missing.');
                return;
            }

            $total = GmailSale::query()->count();
            $completed = GmailSale::query()->where('status', 'completed')->count();
            $pending = GmailSale::query()->where('status', 'pending')->count();
            $rejected = GmailSale::query()->where('status', 'rejected')->count();

            $this->infoLine('Total Gmail rows', (string) $total);
            $this->infoLine('Completed Gmail rows', (string) $completed);
            $this->infoLine('Pending Gmail rows', (string) $pending);
            $this->infoLine('Rejected Gmail rows', (string) $rejected);
        } catch (\Throwable $e) {
            $this->status(false, 'gmail_sales rows', 'error', $e->getMessage());
        }
    }

    private function hasConfigCache(): bool
    {
        return File::exists(base_path('bootstrap/cache/config.php'));
    }

    private function hasRouteCache(): bool
    {
        return count((array) glob(base_path('bootstrap/cache/routes-*.php'))) > 0;
    }

    private function databaseConnectionWorks(): bool
    {
        try {
            DB::connection()->getPdo();
            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    private function databaseVersion(): string
    {
        try {
            $driver = (string) config('database.default');

            if ($driver === 'sqlite') {
                $row = DB::selectOne('select sqlite_version() as version');
                return (string) ($row->version ?? 'unknown');
            }

            $row = DB::selectOne('select version() as version');

            return (string) ($row->version ?? 'unknown');
        } catch (\Throwable $e) {
            return 'unavailable';
        }
    }

    private function databaseName(): string
    {
        try {
            $connection = DB::connection()->getConfig('database');
        } catch (\Throwable $e) {
            return 'unknown';
        }

        return is_string($connection) && $connection !== '' ? $connection : 'unknown';
    }

    private function status(bool $ok, string $label, string $value, string $message = ''): void
    {
        if ($ok) {
            $this->line("[OK] {$label}: {$value}");
            return;
        }

        $this->failures++;
        $suffix = $message !== '' ? " ({$message})" : '';
        $this->error("[FAIL] {$label}: {$value}{$suffix}");
    }

    private function warnUnless(bool $ok, string $label, string $value, string $message): void
    {
        if ($ok) {
            $this->line("[OK] {$label}: {$value}");
            return;
        }

        $this->warnings++;
        $this->warn("[WARN] {$label}: {$value} ({$message})");
    }

    private function warnLine(string $label, string $value, string $message): void
    {
        $this->warnings++;
        $this->warn("[WARN] {$label}: {$value} ({$message})");
    }

    private function infoLine(string $label, string $value): void
    {
        $this->line("[OK] {$label}: {$value}");
    }
}
