<?php

namespace App\Http\Controllers\Install;

use App\Http\Controllers\Controller;
use App\Utils\InstallUtil;
use Composer\Semver\Comparator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Console\Output\BufferedOutput;

class InstallController extends Controller
{
    protected $outputLog;
    protected $appVersion;
    protected $env;

    public function __construct()
    {
        $this->appVersion = config('author.app_version');
        $this->env = config('app.env');
        $this->installSettings();
    }

    private function installSettings(): void
    {
        config(['app.debug' => true]);
        Artisan::call('config:clear');
        Artisan::call('cache:clear');
    }

    private function isInstalled(): void
    {
        if (file_exists(base_path('.env'))) {
            abort(404);
        }
    }

    private function deleteEnv(): bool
    {
        $envPath = base_path('.env');
        if ($envPath && file_exists($envPath)) {
            @unlink($envPath);
        }
        return true;
    }

    public function index()
    {
        $this->isInstalled();
        $this->installSettings();
        return view('install.index');
    }

    public function checkServer()
    {
        $this->isInstalled();
        $this->installSettings();

        $output = [];
        $output['php'] = version_compare(PHP_VERSION, '8.1.0', '>=');
        $output['php_version'] = PHP_VERSION;

        $output['openssl']  = extension_loaded('openssl');
        $output['pdo']      = extension_loaded('pdo');
        $output['mbstring'] = extension_loaded('mbstring');
        $output['tokenizer']= extension_loaded('tokenizer');
        $output['xml']      = extension_loaded('xml');
        $output['curl']     = extension_loaded('curl');
        $output['zip']      = extension_loaded('zip');
        $output['gd']       = extension_loaded('gd');

        $output['storage_writable'] = is_writable(storage_path());
        $output['cache_writable']   = is_writable(base_path('bootstrap/cache'));

        $output['next'] = collect($output)->except(['php_version'])->every(fn ($v) => $v === true);

        return view('install.check-server', compact('output'));
    }

    public function details()
    {
        $this->isInstalled();
        $this->installSettings();

        $env_example = base_path('.env.example');
        if (! file_exists($env_example)) {
            exit("<b>.env.example file not found in <code>$env_example</code></b><br><br>Upload it and refresh this page.");
        }

        return view('install.details')->with('activation_key', false); // no license input
    }

    public function postDetails(Request $request)
    {
        $this->isInstalled();
        $this->installSettings();

        try {
            ini_set('max_execution_time', '0');
            ini_set('memory_limit', '512M');

            $validated = $request->validate([
                'APP_NAME'   => 'required',
                'DB_DATABASE'=> 'required',
                'DB_USERNAME'=> 'required',
                'DB_PASSWORD'=> 'required',
                'DB_HOST'    => 'required',
                'DB_PORT'    => 'required',
            ], [
                'APP_NAME.required'     => 'App Name is required',
                'DB_DATABASE.required'  => 'Database Name is required',
                'DB_USERNAME.required'  => 'Database Username is required',
                'DB_PASSWORD.required'  => 'Database Password is required',
                'DB_HOST.required'      => 'Database Host is required',
                'DB_PORT.required'      => 'Database port is required',
            ]);

            $this->outputLog = new BufferedOutput;

            $input = $request->only([
                'APP_NAME', 'APP_TITLE',
                'DB_HOST','DB_PORT','DB_DATABASE','DB_USERNAME','DB_PASSWORD',
                'MAIL_MAILER','MAIL_FROM_ADDRESS','MAIL_FROM_NAME','MAIL_HOST','MAIL_PORT','MAIL_ENCRYPTION','MAIL_USERNAME','MAIL_PASSWORD',
            ]);

            $input['APP_DEBUG'] = 'false';
            $input['APP_URL']   = url('/');
            $input['APP_ENV']   = 'live';
            $input['LOG_CHANNEL'] = 'daily';

            // DB connectivity test (mysqli)
            $link = @mysqli_connect($input['DB_HOST'], $input['DB_USERNAME'], $input['DB_PASSWORD'], $input['DB_DATABASE'], (int) $input['DB_PORT']);
            if (mysqli_connect_errno()) {
                $msg = '<b>ERROR</b>: Failed to connect to MySQL: '.mysqli_connect_error();
                $msg .= "<br/>Provide correct DB credentials.";
                return back()->with('error', $msg)->withInput();
            }
            @mysqli_close($link);

            $envTemplate = base_path('.env.example');
            $envTarget   = base_path('.env');

            $env_lines = file($envTemplate);

            foreach ($input as $key => $value) {
                $found = false;
                foreach ($env_lines as $i => $line) {
                    if (str_starts_with($line, $key.'=')) {
                        $env_lines[$i] = $key.'="'.str_replace('"', '\"', (string) $value).'"'.PHP_EOL;
                        $found = true;
                        break;
                    }
                }
                if (! $found) {
                    $env_lines[] = $key.'="'.str_replace('"', '\"', (string) $value).'"'.PHP_EOL;
                }
            }

            // Try writing .env directly; if fails, show manual copy view
            $this->deleteEnv();
            $envContent = implode('', $env_lines);
            $written = @file_put_contents($envTarget, $envContent) !== false;

            if (! $written) {
                return view('install.envText', compact('envContent') + ['envPath' => $envTarget]);
            }

            $this->runArtisanCommands();

            return redirect()->route('install.success');
        } catch (\Throwable $e) {
            $this->deleteEnv();
            return back()->with('error', 'Something went wrong, please try again!!')->withInput();
        }
    }

    private function runArtisanCommands(): void
    {
        ini_set('max_execution_time', '0');
        ini_set('memory_limit', '512M');

        $this->installSettings();

        try { Artisan::call('key:generate', ['--force' => true]); } catch (\Throwable $e) {}
        DB::statement('SET default_storage_engine=INNODB;');
        Artisan::call('migrate:fresh', ['--force' => true]);
        Artisan::call('db:seed', ['--force' => true]);
        try { Artisan::call('storage:link'); } catch (\Throwable $e) {}
        Artisan::call('optimize:clear');
    }

    public function installAlternate(Request $request)
    {
        try {
            $this->installSettings();

            if (! file_exists(base_path('.env'))) {
                return redirect()->route('install.details')
                    ->with('error', 'Looks like you haven\'t created the .env file');
            }

            $this->runArtisanCommands();
            return redirect()->route('install.success');
        } catch (\Throwable $e) {
            $this->deleteEnv();
            return back()->with('error', 'Something went wrong, please try again!!');
        }
    }

    public function success()
    {
        return view('install.success');
    }

    public function updateConfirmation()
    {
        $installUtil = new InstallUtil();
        $db_version = $installUtil->getSystemInfo('db_version');

        if (Comparator::greaterThan($this->appVersion, $db_version)) {
            return view('install.update_confirmation');
        }

        exit("<b> Update already done to Version <code>".$db_version."</code></b>");
    }

    public function update(Request $request)
    {
        $version = null;

        try {
            DB::beginTransaction();

            ini_set('max_execution_time', '0');
            ini_set('memory_limit', '512M');

            if (is_null($version)) {
                $installUtil = new InstallUtil();
                $db_version = $installUtil->getSystemInfo('db_version');

                if (Comparator::greaterThan($this->appVersion, $db_version)) {
                    $this->installSettings();
                    DB::statement('SET default_storage_engine=INNODB;');
                    Artisan::call('migrate', ['--force' => true]);
                    Artisan::call('module:publish');
                    try { Artisan::call('passport:install', ['--force' => true]); } catch (\Throwable $e) {}
                    $installUtil->setSystemInfo('db_version', $this->appVersion);
                } else {
                    abort(404);
                }
            } else {
                abort(404);
            }

            DB::commit();

            $output = ['success' => 1, 'msg' => 'Updated Succesfully to version '.$this->appVersion.' !!'];
            return redirect('login')->with('status', $output);
        } catch (\Throwable $e) {
            DB::rollBack();
            exit($e->getMessage());
        }
    }
}
