<?php

namespace Modules\Installer\Http\Controllers;

use App\Forkiva;
use Artisan;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
use Modules\Core\Http\Controllers\Controller;
use Modules\User\Models\User;

class InstallerController extends Controller
{
    private string $tracker;

    public function __construct()
    {
        $this->tracker = storage_path('framework/install.json');
    }

    /**
     * Display a welcome page installer.
     *
     * @throws FileNotFoundException
     */
    public function welcome(): View
    {
        File::delete($this->tracker); // fresh install
        session()->forget('installer');
        $this->markStep('welcome');

        return view('installer.welcome');
    }

    /**
     * @throws FileNotFoundException
     */
    private function markStep(string $step): void
    {
        $data = $this->getSteps();
        $data[$step] = true;
        File::put($this->tracker, json_encode($data));
        session()->put("installer.$step", true);
        session()->save();
    }

    /**
     * @throws FileNotFoundException
     */
    private function getSteps(): array
    {
        if (File::exists($this->tracker)) {
            return json_decode(File::get($this->tracker), true) ?? [];
        }
        return [];
    }

    /**
     * Display system requirements.
     * @throws FileNotFoundException
     */
    public function requirements(): View|RedirectResponse
    {
        if (!$this->stepDone('welcome')) {
            return redirect()->route('installer.welcome');
        }

        $requirements = [
            'PHP >= 8.3' => version_compare(PHP_VERSION, '8.3.0', '>='),
            'ctype' => extension_loaded('ctype'),
            'curl' => extension_loaded('curl'),
            'dom' => extension_loaded('dom'),
            'fileinfo' => extension_loaded('fileinfo'),
            'filter' => extension_loaded('filter'),
            'hash' => extension_loaded('hash'),
            'mbstring' => extension_loaded('mbstring'),
            'openssl' => extension_loaded('openssl'),
            'pcre' => extension_loaded('pcre'),
            'pdo' => extension_loaded('pdo'),
            'session' => extension_loaded('session'),
            'tokenizer' => extension_loaded('tokenizer'),
            'xml' => extension_loaded('xml'),
            'intl' => extension_loaded('intl'),
        ];

        $this->markStep('requirements');

        return view('installer.requirements', compact('requirements'));
    }

    /**
     * @throws FileNotFoundException
     */
    private function stepDone(string $step): bool
    {
        $data = $this->getSteps();
        return $data[$step] ?? false;
    }

    /**
     * Display a permissions page installer.
     *
     * @throws FileNotFoundException
     */
    public function permissions(): View|RedirectResponse
    {
        if (!$this->stepDone('requirements')) {
            return redirect()->route('installer.requirements');
        }

        $paths = [
            ".env" => base_path('.env'),
            "storage" => storage_path(),
            "bootstrap/cache" => base_path('bootstrap/cache'),
        ];

        $permissions = [];
        foreach ($paths as $path) {
            $permissions[$path] = is_writable($path);
        }

        $this->markStep('permissions');

        return view('installer.permissions', ["permissions" => $permissions]);
    }

    /**
     * Handle database setup form.
     *
     * @throws FileNotFoundException
     */
    public function database(Request $request): View|RedirectResponse
    {

        if (!$this->stepDone('permissions')) {
            return redirect()->route('installer.permissions');
        }

        $connections = ["mysql" => "MySql", "pgsql" => "PostgreSQL"];

        if ($request->isMethod('post')) {
            $data = $request->validate([
                'db_connection' => ["required", "string", Rule::in(array_keys($connections))],
                'db_host' => 'required|string',
                'db_port' => 'required|numeric',
                'db_name' => 'required|string',
                'db_user' => 'required|string',
                'db_pass' => 'nullable|string',
            ]);

            // Update .env file
            $envPath = base_path('.env');
            if (!File::exists($envPath)) {
                File::copy(base_path('.env.example'), $envPath);
            }

            $env = File::get($envPath);
            $env = preg_replace('/DB_CONNECTION=.*/', 'DB_CONNECTION=' . $data['db_connection'], $env);
            $env = preg_replace('/DB_HOST=.*/', 'DB_HOST=' . $data['db_host'], $env);
            $env = preg_replace('/DB_PORT=.*/', 'DB_PORT=' . $data['db_port'], $env);
            $env = preg_replace('/DB_DATABASE=.*/', 'DB_DATABASE=' . $data['db_name'], $env);
            $env = preg_replace('/DB_USERNAME=.*/', 'DB_USERNAME=' . $data['db_user'], $env);
            $env = preg_replace('/DB_PASSWORD=.*/', 'DB_PASSWORD=' . $data['db_pass'], $env);
            $env = preg_replace('/APP_SEED_DEMO_DATA=.*/', 'APP_SEED_DEMO_DATA=' . ($request->with_demo ? 'true' : 'false'), $env);

            File::put($envPath, $env);

            config([
                "database.default" => $data['db_connection'],
                "database.connections.{$data['db_connection']}.host" => $data['db_host'],
                "database.connections.{$data['db_connection']}.port" => $data['db_port'],
                "database.connections.{$data['db_connection']}.database" => $data['db_name'],
                "database.connections.{$data['db_connection']}.username" => $data['db_user'],
                "database.connections.{$data['db_connection']}.password" => $data['db_pass'],
                'app.seed_demo_data' => (bool)$request->with_demo,
            ]);

            DB::purge('mysql');
            DB::reconnect('mysql');

            try {
                DB::connection()->getPdo();
            } catch (Exception $e) {
                return back()->withErrors(['db_error' => 'Database connection failed: ' . $e->getMessage()]);
            }

            if (Forkiva::seedDemoData()) {
                set_time_limit(120);
            }

            // Run migrations + seeds
            Artisan::call('key:generate', ['--force' => true]);
            Artisan::call('module:migrate', ['-a' => true, '--force' => true]);
            Artisan::call('module:seed', ['-a' => true, '--force' => true]);

            $this->markStep('database');

            return redirect()->route('installer.admin');
        }

        return view('installer.database', ['connections' => $connections]);
    }

    /**
     * Handle admin user creation.
     *
     * @throws FileNotFoundException
     */
    public function admin(Request $request): View|RedirectResponse
    {
        if (!$this->stepDone('database')) {
            return redirect()->route('installer.database');
        }

        if ($request->isMethod('post')) {
            $data = $request->validate([
                'name' => 'required|string',
                'email' => 'required|email|unique:users,email',
                'password' => [
                    'required',
                    'string',
                    Password::min(8)
                        ->max(20)
                        ->mixedCase()
                        ->numbers()
                        ->symbols(),
                    "confirmed"
                ]
            ]);

            User::query()
                ->where('id', 1)
                ->update([
                    'name' => $data['name'],
                    'email' => $data['email'],
                    'username' => explode('@', $data['email'])[0],
                    'password' => bcrypt($data['password']),
                ]);

            $this->markStep('admin');

            return redirect()->route('installer.finish');
        }

        return view('installer.admin');
    }

    /**
     * Final step - lock installer.
     *
     * @throws FileNotFoundException
     */
    public function finish(): View|RedirectResponse
    {
        if (!$this->stepDone('admin')) {
            return redirect()->route('installer.admin');
        }

        // Mark app as installed
        $envPath = base_path('.env');
        if (File::exists($envPath)) {
            File::append($envPath, "\nAPP_INSTALLED=true\n");
        }

        // Clear wizard progress
        File::delete($this->tracker);
        session()->forget('installer');

        return view('installer.finish');
    }
}
