<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\File;
use App\Models\User;
use App\Models\Setting;

class SetupController extends Controller
{
    // Constructor removed - middleware will handle installation check

    /**
     * Check if database connection exists and tables are created.
     *
     * @return bool
     */
    private function isDatabaseConfigured()
    {
        try {
            // Check if database connection exists
            DB::connection()->getPdo();
            
            // Check if users table exists (indicates database is set up)
            if (Schema::hasTable('users')) {
                return true;
            }
            
            return false;
        } catch (\Exception $e) {
            // Database connection doesn't exist or not configured
            return false;
        }
    }
    
    /**
     * Show the setup wizard welcome page.
     *
     * @return \Illuminate\View\View
     */
    public function index()
    {
        // Allow setup to proceed even if database exists
        // The setup process will detect existing tables and allow user to use existing database
        // Only block if installed.lock file exists (handled by middleware)
        
        return view('setup.welcome');
    }

    /**
     * Check system requirements.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function checkRequirements()
    {
        $requirements = [
            'php_version' => [
                'required' => '8.0',
                'current' => PHP_VERSION,
                'met' => version_compare(PHP_VERSION, '8.0.0', '>='),
            ],
            'extensions' => [
                'pdo' => extension_loaded('pdo'),
                'pdo_mysql' => extension_loaded('pdo_mysql'),
                'mbstring' => extension_loaded('mbstring'),
                'openssl' => extension_loaded('openssl'),
                'tokenizer' => extension_loaded('tokenizer'),
                'xml' => extension_loaded('xml'),
                'json' => extension_loaded('json'),
                'curl' => extension_loaded('curl'),
                'zip' => extension_loaded('zip'),
                'gd' => extension_loaded('gd'),
            ],
            'permissions' => [
                'storage' => is_writable(storage_path()),
                'bootstrap_cache' => is_writable(base_path('bootstrap/cache')),
            ],
        ];

        $allMet = $requirements['php_version']['met'] 
            && !in_array(false, $requirements['extensions'])
            && !in_array(false, $requirements['permissions']);

        return response()->json([
            'requirements' => $requirements,
            'all_met' => $allMet,
        ]);
    }

    /**
     * Test database connection.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function testDatabase(Request $request)
    {
        $request->validate([
            'host' => 'required|string',
            'port' => 'required|string',
            'database' => 'required|string',
            'username' => 'required|string',
            'password' => 'nullable|string',
        ]);

        try {
            config([
                'database.connections.mysql.host' => $request->host,
                'database.connections.mysql.port' => $request->port,
                'database.connections.mysql.database' => $request->database,
                'database.connections.mysql.username' => $request->username,
                'database.connections.mysql.password' => $request->password,
            ]);

            DB::connection('mysql')->getPdo();
            
            // Check if database has existing tables
            $tables = DB::select('SHOW TABLES');
            $tableCount = count($tables);
            $hasUsersTable = Schema::hasTable('users');
            $hasMigrationsTable = Schema::hasTable('migrations');
            
            // Check for pending migrations
            $pendingMigrations = [];
            $migrationStatus = 'unknown';
            
            if ($hasMigrationsTable) {
                // Get list of all migration files
                $migrationFiles = $this->getMigrationFiles();
                
                // Get already run migrations
                $runMigrations = DB::table('migrations')->pluck('migration')->toArray();
                
                // Find pending migrations
                $pendingMigrations = array_diff($migrationFiles, $runMigrations);
                
                if (empty($pendingMigrations)) {
                    $migrationStatus = 'up_to_date';
                } else {
                    $migrationStatus = 'pending';
                }
            } else {
                // No migrations table means all migrations are pending
                $migrationFiles = $this->getMigrationFiles();
                $pendingMigrations = $migrationFiles;
                $migrationStatus = 'pending';
            }
            
            $message = 'Database connection successful!';
            if ($tableCount > 0) {
                $message .= " Found {$tableCount} existing table(s).";
                if ($hasUsersTable) {
                    $message .= " Database appears to be already set up.";
                }
            }
            
            if ($migrationStatus === 'pending') {
                $message .= " " . count($pendingMigrations) . " migration(s) pending.";
            } elseif ($migrationStatus === 'up_to_date') {
                $message .= " All migrations are up to date.";
            }
            
            return response()->json([
                'success' => true,
                'message' => $message,
                'has_tables' => $tableCount > 0,
                'has_users_table' => $hasUsersTable,
                'has_migrations_table' => $hasMigrationsTable,
                'table_count' => $tableCount,
                'migration_status' => $migrationStatus,
                'pending_migrations_count' => count($pendingMigrations),
                'pending_migrations' => array_values($pendingMigrations),
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Database connection failed: ' . $e->getMessage(),
            ], 400);
        }
    }

    /**
     * Get all migration file names (without extension).
     *
     * @return array
     */
    private function getMigrationFiles()
    {
        $migrationPath = database_path('migrations');
        $files = glob($migrationPath . '/*.php');
        
        $migrations = [];
        foreach ($files as $file) {
            $filename = basename($file, '.php');
            // Extract migration name (format: YYYY_MM_DD_HHMMSS_migration_name.php)
            // Migration name is everything after the timestamp
            if (preg_match('/^\d{4}_\d{2}_\d{2}_\d{6}_(.+)$/', $filename, $matches)) {
                $migrations[] = $filename;
            }
        }
        
        return $migrations;
    }

    /**
     * Run the installation process.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function install(Request $request)
    {
        $request->validate([
            'database_host' => 'required|string',
            'database_port' => 'required|string',
            'database_name' => 'required|string',
            'database_username' => 'required|string',
            'database_password' => 'nullable|string',
            'use_existing_database' => 'nullable|boolean',
            'admin_name' => 'required|string|max:255',
            'admin_email' => 'required|email|max:255',
            'admin_password' => 'required|string|min:8|confirmed',
            'app_name' => 'required|string|max:255',
            'app_url' => 'required|url',
        ]);

        try {
            DB::beginTransaction();
            
            // Step 1: Update .env file
            $this->updateEnvFile($request);

            // Step 2: Clear config cache
            Artisan::call('config:clear');
            Artisan::call('cache:clear');

            // Step 3: Test database connection with new settings
            config([
                'database.connections.mysql.host' => $request->database_host,
                'database.connections.mysql.port' => $request->database_port,
                'database.connections.mysql.database' => $request->database_name,
                'database.connections.mysql.username' => $request->database_username,
                'database.connections.mysql.password' => $request->database_password,
            ]);

            // Refresh database connection
            DB::purge('mysql');
            
            // Test connection
            DB::connection('mysql')->getPdo();

            // Step 4: Check if we should use existing database or run migrations
            $useExisting = $request->has('use_existing_database') && $request->use_existing_database;
            $hasUsersTable = Schema::hasTable('users');
            $hasMigrationsTable = Schema::hasTable('migrations');
            
            if ($useExisting && $hasUsersTable) {
                // Using existing database - check if migrations are needed
                if ($hasMigrationsTable) {
                    // Check for pending migrations
                    $migrationFiles = $this->getMigrationFiles();
                    $runMigrations = DB::table('migrations')->pluck('migration')->toArray();
                    $pendingMigrations = array_diff($migrationFiles, $runMigrations);
                    
                    if (!empty($pendingMigrations)) {
                        // Run only pending migrations
                        \Log::info('Using existing database, running ' . count($pendingMigrations) . ' pending migration(s)');
                        Artisan::call('migrate', ['--force' => true]);
                    } else {
                        \Log::info('Using existing database, all migrations are up to date');
                    }
                } else {
                    // No migrations table - create it and run all migrations
                    \Log::info('Using existing database but no migrations table, running migrations');
                    Artisan::call('migrate', ['--force' => true]);
                }
            } else {
                // Fresh installation or database without users table
                // Run migrations (this creates all tables including settings)
                // If tables exist, migrations will only add missing ones
                Artisan::call('migrate', ['--force' => true]);
            }

            // Step 5: Create or update admin user (after migrations ensure users table exists)
            // If using existing database, check if admin already exists
            if ($useExisting && $hasUsersTable) {
                $existingAdmin = User::where('email', $request->admin_email)->first();
                if ($existingAdmin) {
                    // Update existing admin password if provided
                    $existingAdmin->update([
                        'name' => $request->admin_name,
                        'password' => Hash::make($request->admin_password),
                        'role' => 'admin',
                    ]);
                    $admin = $existingAdmin;
                } else {
                    // Create new admin if doesn't exist
                    $admin = User::create([
                        'name' => $request->admin_name,
                        'email' => $request->admin_email,
                        'password' => Hash::make($request->admin_password),
                        'role' => 'admin',
                        'email_verified_at' => now(),
                    ]);
                }
            } else {
                // Normal installation - create or update admin
                $admin = User::updateOrCreate(
                    ['email' => $request->admin_email],
                    [
                        'name' => $request->admin_name,
                        'password' => Hash::make($request->admin_password),
                        'role' => 'admin',
                        'email_verified_at' => now(),
                    ]
                );
            }

            // Step 6: Set initial settings (after migrations ensure settings table exists)
            try {
                Setting::set('system_name', $request->app_name);
                Setting::set('mail_from_address', $request->admin_email);
                Setting::set('mail_from_name', $request->app_name);
            } catch (\Exception $e) {
                // Settings table might not exist yet, skip for now
                \Log::warning('Could not set initial settings: ' . $e->getMessage());
            }

            // Step 7: Create installed lock file
            File::put(storage_path('app/installed.lock'), date('Y-m-d H:i:s'));

            DB::commit();

            // Step 8: Clear and rebuild caches
            try {
                Artisan::call('config:cache');
                Artisan::call('route:cache');
                Artisan::call('view:cache');
            } catch (\Exception $e) {
                // Cache commands might fail, but that's okay
                \Log::warning('Cache commands failed: ' . $e->getMessage());
            }

            return response()->json([
                'success' => true,
                'message' => 'Installation completed successfully!',
                'admin_email' => $request->admin_email,
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => 'Installation failed: ' . $e->getMessage() . ' (Line: ' . $e->getLine() . ')',
            ], 500);
        }
    }

    /**
     * Update .env file with provided values.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    private function updateEnvFile(Request $request)
    {
        $envPath = base_path('.env');
        
        if (!File::exists($envPath)) {
            // Copy from .env.example if .env doesn't exist
            if (File::exists(base_path('.env.example'))) {
                File::copy(base_path('.env.example'), $envPath);
            } else {
                // Create basic .env file
                $this->createEnvFile($request);
                return;
            }
        }

        $envContent = File::get($envPath);

        // Update database settings
        $envContent = preg_replace('/DB_HOST=.*/', 'DB_HOST=' . $request->database_host, $envContent);
        $envContent = preg_replace('/DB_PORT=.*/', 'DB_PORT=' . $request->database_port, $envContent);
        $envContent = preg_replace('/DB_DATABASE=.*/', 'DB_DATABASE=' . $request->database_name, $envContent);
        $envContent = preg_replace('/DB_USERNAME=.*/', 'DB_USERNAME=' . $request->database_username, $envContent);
        $envContent = preg_replace('/DB_PASSWORD=.*/', 'DB_PASSWORD=' . ($request->database_password ?: ''), $envContent);

        // Update app settings
        $appUrl = rtrim($request->app_url, '/');
        $envContent = preg_replace('/APP_NAME=.*/', 'APP_NAME="' . addslashes($request->app_name) . '"', $envContent);
        $envContent = preg_replace('/APP_URL=.*/', 'APP_URL=' . $appUrl, $envContent);
        $envContent = preg_replace('/APP_ENV=.*/', 'APP_ENV=production', $envContent);
        $envContent = preg_replace('/APP_DEBUG=.*/', 'APP_DEBUG=false', $envContent);

        // Generate application key if not exists or is empty
        $hasAppKey = preg_match('/APP_KEY=(.+)/', $envContent, $keyMatches);
        $appKeyValue = $hasAppKey ? trim($keyMatches[1] ?? '') : '';
        
        if (!$hasAppKey || empty($appKeyValue) || $appKeyValue === 'Your-Key-Here' || strpos($appKeyValue, 'base64:') === false) {
            // Generate a new key
            $key = 'base64:' . base64_encode(random_bytes(32));
            if ($hasAppKey) {
                $envContent = preg_replace('/APP_KEY=.*/', 'APP_KEY=' . $key, $envContent);
            } else {
                $envContent = 'APP_KEY=' . $key . "\n" . $envContent;
            }
        }

        File::put($envPath, $envContent);
    }

    /**
     * Create basic .env file.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    private function createEnvFile(Request $request)
    {
        $appKey = 'base64:' . base64_encode(random_bytes(32));
        $appUrl = rtrim($request->app_url, '/');

        $envContent = "APP_NAME=\"{$request->app_name}\"
APP_ENV=production
APP_KEY={$appKey}
APP_DEBUG=false
APP_URL={$appUrl}

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error

DB_CONNECTION=mysql
DB_HOST={$request->database_host}
DB_PORT={$request->database_port}
DB_DATABASE={$request->database_name}
DB_USERNAME={$request->database_username}
DB_PASSWORD={$request->database_password}

BROADCAST_DRIVER=log
CACHE_DRIVER=redis
FILESYSTEM_DISK=local
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=1440
SESSION_SECURE_COOKIE=true

MEMCACHED_HOST=127.0.0.1

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=\"{$request->admin_email}\"
MAIL_FROM_NAME=\"{$request->app_name}\"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME=\"{$request->app_name}\"
";

        File::put(base_path('.env'), $envContent);
    }
}

