<?php

namespace App\Http\Controllers\Installer;

use App\Http\Controllers\Controller;
use App\Http\Requests\Installer\AdminRequest;
use App\Http\Requests\Installer\CompanyRequest;
use App\Http\Requests\Installer\DatabaseRequest;
use App\Http\Requests\Installer\MailRequest;
use App\Http\Requests\Installer\WelcomeRequest;
use App\Services\Installer\DatabaseConnectionTester;
use App\Services\Installer\InstallActivationAction;
use App\Services\Installer\InstallFinalizeAction;
use App\Services\Installer\InstallMigrationAction;
use App\Services\Installer\ServerRequirementsChecker;
use App\Services\Installer\SmtpConnectionTester;
use App\Services\Installer\WizardSessionStore;
use App\Support\EnvWriter;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Throwable;

class InstallWizardController extends Controller
{
    public function welcome(WizardSessionStore $wizard): View
    {
        return view('installer.welcome', [
            'state' => $wizard->get(),
            'version' => config('installer.version'),
            'steps' => $this->steps(),
            'currentStep' => 1,
        ]);
    }

    public function storeWelcome(WelcomeRequest $request, WizardSessionStore $wizard): RedirectResponse
    {
        $wizard->merge([
            'app_name' => $request->validated('app_name'),
            'app_url' => $request->validated('app_url'),
            'installation_id' => $wizard->get()->installationId ?? (string) Str::uuid(),
            'current_step' => 2,
        ]);

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

    public function requirements(ServerRequirementsChecker $checker, WizardSessionStore $wizard): View
    {
        $results = $checker->check();

        return view('installer.requirements', [
            'results' => $results,
            'requirements' => $this->formatRequirements($results),
            'extensions' => $this->formatExtensions($results),
            'canContinue' => $results['passed'],
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 2,
        ]);
    }

    public function storeRequirements(WizardSessionStore $wizard, ServerRequirementsChecker $checker): RedirectResponse
    {
        $results = $checker->check();

        if (! $results['passed']) {
            return back()->with('error', 'Server requirements are not met.');
        }

        $wizard->merge(['requirements_passed' => true, 'current_step' => 3]);

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

    public function database(WizardSessionStore $wizard): View
    {
        return view('installer.database', [
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 3,
        ]);
    }

    public function storeDatabase(
        DatabaseRequest $request,
        WizardSessionStore $wizard,
        DatabaseConnectionTester $tester,
        InstallMigrationAction $migration,
        EnvWriter $envWriter,
    ): RedirectResponse {
        $data = $request->validated();
        $result = $tester->test([
            'connection' => $data['db_connection'],
            'host' => $data['db_host'] ?? null,
            'port' => $data['db_port'] ?? null,
            'database' => $data['db_database'],
            'username' => $data['db_username'] ?? null,
            'password' => $data['db_password'] ?? null,
        ]);

        if (! $result['success']) {
            return back()->withInput()->with('error', $result['message'] ?? 'Database connection failed.');
        }

        $previous = $wizard->get();

        $state = $wizard->merge([
            'db_connection' => $data['db_connection'],
            'db_host' => $data['db_host'] ?? null,
            'db_port' => $data['db_port'] ?? null,
            'db_database' => $data['db_database'],
            'db_username' => $data['db_username'] ?? null,
            'db_password' => $data['db_password'] ?? null,
            'current_step' => 4,
        ]);

        $shouldMigrate = ! $previous->migrationsRun || ! $previous->matchesDatabase($state);

        if ($shouldMigrate) {
            try {
                $migration->execute($state);
            } catch (Throwable $e) {
                return back()->withInput()->with('error', $e->getMessage());
            }

            $wizard->merge(['migrations_run' => true]);
        }

        $envWriter->setMany([
            'DB_CONNECTION' => $data['db_connection'],
            'DB_HOST' => $data['db_host'] ?? '127.0.0.1',
            'DB_PORT' => (string) ($data['db_port'] ?? 3306),
            'DB_DATABASE' => $data['db_database'],
            'DB_USERNAME' => $data['db_username'] ?? 'root',
            'DB_PASSWORD' => $data['db_password'] ?? '',
            'SESSION_DRIVER' => 'file',
            'CACHE_STORE' => 'file',
            'QUEUE_CONNECTION' => 'sync',
        ]);

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

    public function mail(WizardSessionStore $wizard): View
    {
        return view('installer.mail', [
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 4,
        ]);
    }

    public function storeMail(MailRequest $request, WizardSessionStore $wizard, SmtpConnectionTester $tester): RedirectResponse
    {
        if ($request->input('action') === 'skip') {
            $state = $wizard->get();
            $wizard->merge([
                'mail_mailer' => 'log',
                'mail_from_address' => 'noreply@localhost',
                'mail_from_name' => $state->appName ?? 'Admin',
                'current_step' => 5,
            ]);

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

        $data = $request->validated();

        if (($data['mail_mailer'] ?? 'smtp') === 'smtp') {
            $result = $tester->test([
                'mailer' => $data['mail_mailer'],
                'host' => $data['mail_host'],
                'port' => $data['mail_port'] ?? 587,
                'username' => $data['mail_username'] ?? null,
                'password' => $data['mail_password'] ?? null,
                'encryption' => $data['mail_encryption'] ?? 'tls',
            ]);

            if (! $result['success']) {
                return back()->withInput()->with('error', $result['message'] ?? 'SMTP connection failed.');
            }
        }

        $wizard->merge([
            'mail_mailer' => $data['mail_mailer'],
            'mail_host' => $data['mail_host'] ?? null,
            'mail_port' => $data['mail_port'] ?? null,
            'mail_username' => $data['mail_username'] ?? null,
            'mail_password' => $data['mail_password'] ?? null,
            'mail_encryption' => $data['mail_encryption'] ?? null,
            'mail_from_address' => $data['mail_from_address'],
            'mail_from_name' => $data['mail_from_name'],
            'current_step' => 5,
        ]);

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

    public function company(WizardSessionStore $wizard): View
    {
        return view('installer.company', [
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 5,
        ]);
    }

    public function storeCompany(CompanyRequest $request, WizardSessionStore $wizard): RedirectResponse
    {
        $wizard->merge(array_merge($request->validated(), ['current_step' => 6]));

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

    public function admin(WizardSessionStore $wizard): View
    {
        return view('installer.admin', [
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 6,
        ]);
    }

    public function storeAdmin(AdminRequest $request, WizardSessionStore $wizard, InstallActivationAction $activation): RedirectResponse
    {
        $current = $wizard->get();

        $wizard->merge(array_merge($request->validated(), [
            'current_step' => 7,
            'installation_id' => $current->installationId ?? (string) Str::uuid(),
        ]));

        try {
            $state = $activation->execute($wizard->get());
        } catch (Throwable $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        $redirect = redirect()->route('installer.review');

        if (str_starts_with($state->activationToken ?? '', 'local-')) {
            $redirect->with('warning', 'Product Owner platform is unavailable. Setup will continue without remote activation.');
        }

        return $redirect;
    }

    public function review(WizardSessionStore $wizard): View
    {
        return view('installer.review', [
            'state' => $wizard->get(),
            'steps' => $this->steps(),
            'currentStep' => 7,
        ]);
    }

    public function finalize(WizardSessionStore $wizard, InstallFinalizeAction $finalize): RedirectResponse
    {
        @set_time_limit(120);

        $state = $wizard->get();

        try {
            $finalize->execute($state);
        } catch (Throwable $e) {
            return back()->with('error', $e->getMessage());
        }

        $summary = [
            'app_name' => $state->appName,
            'admin_email' => $state->adminEmail,
            'version' => config('installer.version'),
            'installation_id' => $state->installationId,
            'activation_status' => str_starts_with($state->activationToken ?? '', 'local-') ? 'skipped' : 'activated',
        ];

        return redirect()
            ->route('installer.complete')
            ->with('installer_summary', $summary);
    }

    public function complete(): View
    {
        return view('installer.complete', [
            'summary' => session('installer_summary', []),
            'steps' => $this->steps(),
            'currentStep' => 7,
        ]);
    }

    /**
     * @return list<string>
     */
    private function steps(): array
    {
        return ['Welcome', 'Requirements', 'Database', 'SMTP', 'Company', 'Admin', 'Review'];
    }

    /**
     * @param  array<string, mixed>  $results
     * @return list<array<string, string>>
     */
    private function formatRequirements(array $results): array
    {
        $items = [[
            'name' => 'PHP Version',
            'current' => $results['php_version']['current'] ?? PHP_VERSION,
            'required' => $results['php_version']['required'] ?? '8.2.0',
            'status' => ($results['php_version']['passed'] ?? false) ? 'pass' : 'fail',
        ]];

        foreach ($results['writable_paths'] ?? [] as $path => $info) {
            $items[] = [
                'name' => 'Writable: '.$path,
                'current' => ($info['passed'] ?? false) ? 'Writable' : 'Not writable',
                'required' => 'Writable',
                'status' => ($info['passed'] ?? false) ? 'pass' : 'fail',
            ];
        }

        return $items;
    }

    /**
     * @param  array<string, mixed>  $results
     * @return list<array<string, string>>
     */
    private function formatExtensions(array $results): array
    {
        $extensions = [];

        foreach ($results['extensions'] ?? [] as $name => $info) {
            $extensions[] = [
                'name' => $name,
                'status' => ($info['passed'] ?? false) ? 'pass' : 'fail',
            ];
        }

        return $extensions;
    }
}
