<?php

namespace App\Services\Installation;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

class SystemHealthService
{
    /**
     * Get the current Ticaga version from lock file.
     */
    public function getCurrentVersion(): ?string
    {
        $lockPath = storage_path('installed.lock');

        if (! file_exists($lockPath)) {
            return null;
        }

        $lockData = json_decode(file_get_contents($lockPath), true);

        return $lockData['version'] ?? null;
    }

    /**
     * Check overall system health and return status array.
     */
    public function checkSystemHealth(): array
    {
        return [
            'installed' => $this->isInstalled(),
            'version' => $this->getCurrentVersion(),
            'files' => $this->checkRequiredFiles(),
            'database' => $this->checkDatabaseHealth(),
            'services' => $this->checkRequiredServices(),
            'permissions' => $this->checkPermissions(),
            'dependencies' => $this->checkDependencies(),
            'extensions' => $this->checkExtensions(),
        ];
    }

    /**
     * Check if Ticaga is installed.
     */
    public function isInstalled(): bool
    {
        return file_exists(storage_path('installed.lock'));
    }

    /**
     * Check if all required core files exist.
     */
    public function checkRequiredFiles(): array
    {
        $requiredFiles = $this->getRequiredFileManifest();
        $missing = [];
        $present = [];

        foreach ($requiredFiles as $file) {
            $fullPath = base_path($file);

            if (file_exists($fullPath)) {
                $present[] = $file;
            } else {
                $missing[] = $file;
            }
        }

        return [
            'status' => empty($missing) ? 'ok' : 'warning',
            'total' => count($requiredFiles),
            'present' => count($present),
            'missing' => count($missing),
            'missing_files' => $missing,
        ];
    }

    /**
     * Check database connectivity and status.
     */
    public function checkDatabaseHealth(): array
    {
        try {
            DB::connection()->getPdo();
            $connected = true;
            $error = null;
        } catch (\Throwable $exception) {
            $connected = false;
            $error = $exception->getMessage();
        }

        $hasMigrations = false;
        $hasUsers = false;
        $pendingMigrations = [];

        if ($connected) {
            try {
                $hasMigrations = DB::getSchemaBuilder()->hasTable('migrations');
                $hasUsers = DB::getSchemaBuilder()->hasTable('users') && DB::table('users')->exists();

                // Check for pending migrations
                $ran = DB::table('migrations')->pluck('migration')->toArray();
                $migrationFiles = $this->getMigrationFiles();
                $pendingMigrations = array_diff($migrationFiles, $ran);
            } catch (\Throwable $exception) {
                // Ignore errors if tables don't exist yet
            }
        }

        return [
            'status' => $connected && $hasUsers ? 'ok' : ($connected ? 'warning' : 'error'),
            'connected' => $connected,
            'has_migrations_table' => $hasMigrations,
            'has_users' => $hasUsers,
            'pending_migrations' => count($pendingMigrations),
            'pending_migration_list' => array_values($pendingMigrations),
            'error' => $error,
        ];
    }

    /**
     * Check if required services are running.
     */
    public function checkRequiredServices(): array
    {
        $services = [
            'mysql' => $this->checkMysqlRunning(),
            'php_fpm' => $this->checkPhpFpmRunning(),
        ];

        $allRunning = collect($services)->every(fn ($status) => $status === true);

        return [
            'status' => $allRunning ? 'ok' : 'warning',
            'services' => $services,
        ];
    }

    /**
     * Check storage and cache directory permissions.
     */
    public function checkPermissions(): array
    {
        $directories = [
            'storage' => storage_path(),
            'storage/logs' => storage_path('logs'),
            'storage/framework' => storage_path('framework'),
            'bootstrap/cache' => base_path('bootstrap/cache'),
        ];

        $permissions = [];
        $allWritable = true;

        foreach ($directories as $name => $path) {
            $writable = file_exists($path) && is_writable($path);
            $permissions[$name] = [
                'writable' => $writable,
                'path' => $path,
            ];

            if (! $writable) {
                $allWritable = false;
            }
        }

        return [
            'status' => $allWritable ? 'ok' : 'error',
            'directories' => $permissions,
        ];
    }

    /**
     * Check composer and npm dependencies.
     */
    public function checkDependencies(): array
    {
        $composerInstalled = file_exists(base_path('vendor/autoload.php'));
        $npmInstalled = is_dir(base_path('node_modules'));

        return [
            'status' => ($composerInstalled && $npmInstalled) ? 'ok' : 'warning',
            'composer' => $composerInstalled,
            'npm' => $npmInstalled,
        ];
    }

    /**
     * Check extension system status.
     */
    public function checkExtensions(): array
    {
        if (! $this->isInstalled()) {
            return [
                'status' => 'pending',
                'message' => 'Extension check pending until installation completes',
            ];
        }

        try {
            $extensionsDir = app_path('Extensions/Installed');
            $extensionCount = is_dir($extensionsDir) ? count(File::directories($extensionsDir)) : 0;

            return [
                'status' => 'ok',
                'installed' => $extensionCount,
                'directory_exists' => is_dir($extensionsDir),
            ];
        } catch (\Throwable $exception) {
            return [
                'status' => 'error',
                'error' => $exception->getMessage(),
            ];
        }
    }

    /**
     * Get list of required core files.
     */
    protected function getRequiredFileManifest(): array
    {
        return [
            // Core Controllers
            'app/Http/Controllers/Controller.php',
            'app/Http/Controllers/InstallController.php',
            'app/Http/Controllers/TicketsController.php',

            // Core Services
            'app/Services/Installation/InstallationService.php',

            // Core Providers
            'app/Providers/AppServiceProvider.php',
            'app/Providers/ExtensionServiceProvider.php',

            // Installer Views
            'resources/views/install/index.blade.php',

            // Core Views
            'resources/views/dashboard.blade.php',
            'resources/views/components/layouts/app.blade.php',

            // Models
            'app/Models/User.php',
            'app/Models/Tickets.php',
            'app/Models/Responses.php',
            'app/Models/Departments.php',

            // Config
            'config/app.php',
            'config/ticaga.php',
            'config/database.php',

            // Bootstrap
            'bootstrap/app.php',
            'bootstrap/providers.php',
        ];
    }

    /**
     * Get list of migration files.
     */
    protected function getMigrationFiles(): array
    {
        $migrationPath = database_path('migrations');

        if (! is_dir($migrationPath)) {
            return [];
        }

        $files = File::files($migrationPath);
        $migrations = [];

        foreach ($files as $file) {
            if ($file->getExtension() === 'php') {
                $migrations[] = str_replace('.php', '', $file->getFilename());
            }
        }

        return $migrations;
    }

    /**
     * Check if MySQL is running.
     */
    protected function checkMysqlRunning(): bool
    {
        try {
            DB::connection()->getPdo();

            return true;
        } catch (\Throwable $exception) {
            return false;
        }
    }

    /**
     * Check if PHP-FPM is running.
     */
    protected function checkPhpFpmRunning(): bool
    {
        // PHP-FPM is running if this code executes
        return true;
    }

    /**
     * Check if an upgrade is available by comparing config version to installed version.
     */
    public function isUpgradeAvailable(): array
    {
        $configVersion = config('ticaga.version', '0.0.0');
        $installedVersion = $this->getCurrentVersion() ?? '0.0.0';

        // Compare versions using version_compare
        $upgradeAvailable = version_compare($configVersion, $installedVersion, '>');

        return [
            'upgrade_available' => $upgradeAvailable,
            'config_version' => $configVersion,
            'installed_version' => $installedVersion,
        ];
    }

    /**
     * Get overall health status summary.
     */
    public function getHealthSummary(): array
    {
        $health = $this->checkSystemHealth();
        $upgradeInfo = $this->isUpgradeAvailable();

        $issues = [];
        $warnings = [];

        if ($health['files']['status'] !== 'ok') {
            $warnings[] = sprintf('%d required files are missing', $health['files']['missing']);
        }

        if ($health['database']['status'] === 'error') {
            $issues[] = 'Database connection failed: ' . $health['database']['error'];
        } elseif ($health['database']['status'] === 'warning') {
            if (! $health['database']['has_users']) {
                $warnings[] = 'No users found in database';
            }
        }

        if ($health['database']['pending_migrations'] > 0) {
            $warnings[] = sprintf('%d pending database migrations', $health['database']['pending_migrations']);
        }

        if ($health['services']['status'] !== 'ok') {
            $warnings[] = 'Some required services are not running';
        }

        if ($health['permissions']['status'] !== 'ok') {
            $issues[] = 'Storage directories are not writable';
        }

        if ($health['dependencies']['status'] !== 'ok') {
            $warnings[] = 'Dependencies need to be installed';
        }

        // Check for available upgrades
        if ($upgradeInfo['upgrade_available']) {
            $warnings[] = sprintf(
                'Upgrade available: %s → %s',
                $upgradeInfo['installed_version'],
                $upgradeInfo['config_version']
            );
        }

        $overallStatus = 'ok';
        if (! empty($issues)) {
            $overallStatus = 'error';
        } elseif (! empty($warnings)) {
            $overallStatus = 'warning';
        }

        return [
            'status' => $overallStatus,
            'installed' => $health['installed'],
            'version' => $health['version'],
            'upgrade' => $upgradeInfo,
            'issues' => $issues,
            'warnings' => $warnings,
            'checks' => $health,
        ];
    }
}
