<?php

namespace App\Extensions;

use App\Extensions\Contracts\ExtensionInterface;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Route;

abstract class Extension implements ExtensionInterface
{
    protected string $path;
    protected array $manifest = [];
    protected ?\App\Models\Extension $extension = null;

    public function __construct(string $path)
    {
        $this->path = $path;
        $this->loadManifest();
    }

    /**
     * Load the extension manifest (extension.json)
     */
    protected function loadManifest(): void
    {
        $manifestPath = $this->path . '/extension.json';

        if (File::exists($manifestPath)) {
            $this->manifest = json_decode(File::get($manifestPath), true) ?? [];
        }
    }

    /**
     * Get the extension's path
     */
    public function getPath(): string
    {
        return $this->path;
    }

    /**
     * Get the extension's unique identifier (slug)
     */
    public function getId(): string
    {
        return $this->manifest['id'] ?? basename($this->path);
    }

    /**
     * Get the extension's display name
     */
    public function getName(): string
    {
        return $this->manifest['name'] ?? $this->getId();
    }

    /**
     * Get the extension's description
     */
    public function getDescription(): string
    {
        return $this->manifest['description'] ?? '';
    }

    /**
     * Get the extension's version
     */
    public function getVersion(): string
    {
        return $this->manifest['version'] ?? '1.0.0';
    }

    /**
     * Get the extension's author
     */
    public function getAuthor(): string
    {
        return $this->manifest['author'] ?? '';
    }

    /**
     * Get the minimum required Ticaga version
     */
    public function getRequiredTicagaVersion(): ?string
    {
        return $this->manifest['requires']['ticaga'] ?? null;
    }

    /**
     * Get extension requirements.
     * Override this method in your extension to specify requirements.
     *
     * @return array{
     *     php?: string,
     *     ticaga?: string,
     *     laravel?: string,
     *     extensions?: string[],
     *     packages?: array<string, string>,
     *     config?: array<string, mixed>,
     *     services?: string[]
     * }
     */
    public function getRequirements(): array
    {
        return [
            'php' => $this->manifest['requires']['php'] ?? null,
            'ticaga' => $this->manifest['requires']['ticaga'] ?? null,
            'laravel' => $this->manifest['requires']['laravel'] ?? null,
            'extensions' => $this->manifest['requires']['extensions'] ?? [],
            'packages' => $this->manifest['requires']['packages'] ?? [],
            'config' => $this->manifest['requires']['config'] ?? [],
            'services' => $this->manifest['requires']['services'] ?? [],
        ];
    }

    /**
     * Check if all requirements are met.
     *
     * @return array{passed: bool, errors: string[], warnings: string[]}
     */
    public function checkRequirements(): array
    {
        $requirements = $this->getRequirements();
        $errors = [];
        $warnings = [];

        // Check PHP version
        if (!empty($requirements['php'])) {
            if (version_compare(PHP_VERSION, $requirements['php'], '<')) {
                $errors[] = "PHP {$requirements['php']} or higher required (current: " . PHP_VERSION . ")";
            }
        }

        // Check Ticaga version
        if (!empty($requirements['ticaga'])) {
            $ticagaVersion = config('app.version', '1.0.0');
            if (version_compare($ticagaVersion, $requirements['ticaga'], '<')) {
                $errors[] = "Ticaga {$requirements['ticaga']} or higher required (current: {$ticagaVersion})";
            }
        }

        // Check Laravel version
        if (!empty($requirements['laravel'])) {
            $laravelVersion = app()->version();
            if (version_compare($laravelVersion, $requirements['laravel'], '<')) {
                $errors[] = "Laravel {$requirements['laravel']} or higher required (current: {$laravelVersion})";
            }
        }

        // Check required extensions
        if (!empty($requirements['extensions'])) {
            foreach ($requirements['extensions'] as $requiredExtension) {
                $exists = \App\Models\Extension::where('extension_id', $requiredExtension)
                    ->where('installed', true)
                    ->where('enabled', true)
                    ->exists();

                if (!$exists) {
                    $errors[] = "Extension '{$requiredExtension}' must be installed and enabled";
                }
            }
        }

        // Check required packages
        if (!empty($requirements['packages'])) {
            foreach ($requirements['packages'] as $package => $version) {
                if (!class_exists($package) && !interface_exists($package)) {
                    $errors[] = "Package '{$package}' is required";
                }
            }
        }

        // Check required config
        if (!empty($requirements['config'])) {
            foreach ($requirements['config'] as $configKey => $expectedValue) {
                $actualValue = config($configKey);
                if ($actualValue !== $expectedValue) {
                    $warnings[] = "Config '{$configKey}' should be set to '{$expectedValue}'";
                }
            }
        }

        // Check required services
        if (!empty($requirements['services'])) {
            foreach ($requirements['services'] as $service) {
                if (!app()->bound($service)) {
                    $warnings[] = "Service '{$service}' is not registered";
                }
            }
        }

        return [
            'passed' => empty($errors),
            'errors' => $errors,
            'warnings' => $warnings,
        ];
    }

    /**
     * Called when the extension is installed
     */
    public function install(): void
    {
        $migrationsPath = $this->path . '/database/migrations';

        if (!File::isDirectory($migrationsPath)) {
            return;
        }

        // Clean up any orphaned migration records from failed installations
        $this->cleanupOrphanedMigrations($migrationsPath);

        // Ensure the framework knows about the migration path
        $this->registerMigrations();

        // Run any pending migrations immediately so required tables exist
        $this->runMigrations($migrationsPath);
    }

    /**
     * Called when the extension is enabled
     */
    public function enable(): void
    {
        // Default implementation - can be overridden
    }

    /**
     * Called when the extension is disabled
     */
    public function disable(): void
    {
        // Default implementation - can be overridden
    }

    /**
     * Called when the extension is uninstalled
     */
    public function uninstall(): void
    {
        // Default implementation - can be overridden
    }

    /**
     * Called when the extension is upgraded
     *
     * @param string $fromVersion The version being upgraded from
     * @param string $toVersion The version being upgraded to
     */
    public function upgrade(string $fromVersion, string $toVersion): void
    {
        // Default implementation - can be overridden
        // Extensions can implement custom upgrade logic here
    }

    /**
     * Register extension routes
     */
    public function registerRoutes(): void
    {
        $routesPath = $this->path . '/routes/web.php';

        if (File::exists($routesPath)) {
            Route::middleware(['web', 'auth'])
                ->prefix('extensions/' . $this->getId())
                ->group($routesPath);
        }
    }

    /**
     * Register extension views
     */
    public function registerViews(): void
    {
        $viewsPath = $this->path . '/views';

        if (File::isDirectory($viewsPath)) {
            app('view')->addNamespace('extension.' . $this->getId(), $viewsPath);
        }
    }

    /**
     * Register extension migrations
     */
    public function registerMigrations(): void
    {
        $migrationsPath = $this->path . '/database/migrations';

        if (File::isDirectory($migrationsPath)) {
            app('migrator')->path($migrationsPath);
        }
    }

    /**
     * Run pending migrations for the extension.
     */
    protected function runMigrations(string $migrationsPath): void
    {
        if (!File::isDirectory($migrationsPath)) {
            return;
        }

        // Get the relative path from the Laravel base path for Artisan command
        $relativePath = str_replace(base_path() . '/', '', $migrationsPath);

        // Run migrations using Artisan command for better reliability
        \Illuminate\Support\Facades\Artisan::call('migrate', [
            '--path' => $relativePath,
            '--force' => true, // Run in production without confirmation
        ]);

        // Check if migrations ran successfully
        $output = \Illuminate\Support\Facades\Artisan::output();

        if (str_contains($output, 'SQLSTATE') || str_contains($output, 'Exception')) {
            throw new \RuntimeException("Migration failed: " . $output);
        }
    }

    /**
     * Clean up orphaned migration records from failed installations.
     * This method checks if tables exist for each migration record and removes
     * the migration record if the corresponding tables don't exist.
     */
    protected function cleanupOrphanedMigrations(string $migrationsPath): void
    {
        if (!File::isDirectory($migrationsPath)) {
            return;
        }

        try {
            // Get all migration files for this extension
            $migrationFiles = File::files($migrationsPath);

            foreach ($migrationFiles as $file) {
                $migrationName = str_replace('.php', '', $file->getFilename());

                // Check if this migration is recorded as run
                $migrationExists = \Illuminate\Support\Facades\DB::table('migrations')
                    ->where('migration', $migrationName)
                    ->exists();

                if (!$migrationExists) {
                    continue;
                }

                // Extract potential table names from migration name
                // Common patterns: create_xxx_table, add_xxx_to_yyy_table
                if (preg_match('/create_(.+?)_table/', $migrationName, $matches)) {
                    $tableName = $matches[1];

                    // Check if the table exists
                    if (!\Illuminate\Support\Facades\Schema::hasTable($tableName)) {
                        // Migration is recorded but table doesn't exist - clean it up
                        \Illuminate\Support\Facades\DB::table('migrations')
                            ->where('migration', $migrationName)
                            ->delete();
                        \Illuminate\Support\Facades\Log::info("Cleaned up orphaned migration record: {$migrationName} (table '{$tableName}' not found)");
                    }
                }
            }
        } catch (\Exception $e) {
            // Log but don't fail if cleanup has issues
            \Illuminate\Support\Facades\Log::warning("Failed to cleanup orphaned migrations: " . $e->getMessage());
        }
    }

    /**
     * Get extension configuration
     */
    public function getConfig(): array
    {
        $configPath = $this->path . '/config/config.php';

        if (File::exists($configPath)) {
            return require $configPath;
        }

        return $this->manifest['config'] ?? [];
    }

    /**
     * Check if extension is compatible with current Ticaga version
     */
    public function isCompatible(): bool
    {
        $requiredVersion = $this->getRequiredTicagaVersion();

        if (!$requiredVersion) {
            return true;
        }

        $currentVersion = config('app.version', '2.2.0');

        return version_compare($currentVersion, $requiredVersion, '>=');
    }

    /**
     * Get manifest data
     */
    public function getManifest(): array
    {
        return $this->manifest;
    }

    /**
     * Check if extension has a specific capability
     */
    public function hasCapability(string $capability): bool
    {
        return in_array($capability, $this->manifest['capabilities'] ?? []);
    }

    /**
     * Get extension icon (if specified)
     */
    public function getIcon(): ?string
    {
        return $this->manifest['icon'] ?? null;
    }

    /**
     * Get extension homepage URL (if specified)
     */
    public function getHomepage(): ?string
    {
        return $this->manifest['homepage'] ?? null;
    }
}
