<?php

namespace App\Services\Core;

use App\Contracts\TelegramModuleInterface;
use App\DTOs\TelegramMessage;
use App\DTOs\TelegramCallback;
use App\DTOs\TelegramResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;

class ModuleManager
{
    /** @var Collection<TelegramModuleInterface> */
    private Collection $modules;

    /** @var array */
    private array $modulesByName = [];

    public function __construct()
    {
        $this->modules = collect();
    }

    /**
     * Register a module with the manager
     */
    public function register(TelegramModuleInterface $module): void
    {
        $name = $module->getModuleName();
        
        if (isset($this->modulesByName[$name])) {
            Log::warning("Module {$name} is already registered, replacing existing instance");
        }

        // Check dependencies
        $this->validateDependencies($module);

        $this->modules->push($module);
        $this->modulesByName[$name] = $module;

        Log::info("Module registered: {$name} v{$module->getModuleVersion()}");
    }

    /**
     * Unregister a module
     */
    public function unregister(string $moduleName): void
    {
        if (!isset($this->modulesByName[$moduleName])) {
            return;
        }

        $module = $this->modulesByName[$moduleName];
        
        // Check if other modules depend on this one
        $dependents = $this->findDependents($moduleName);
        if (!empty($dependents)) {
            throw new \RuntimeException(
                "Cannot unregister module {$moduleName}. Following modules depend on it: " . 
                implode(', ', $dependents)
            );
        }

        $module->uninstall();
        
        $this->modules = $this->modules->reject(
            fn(TelegramModuleInterface $m) => $m->getModuleName() === $moduleName
        );
        
        unset($this->modulesByName[$moduleName]);

        Log::info("Module unregistered: {$moduleName}");
    }

    /**
     * Get all registered modules ordered by priority
     */
    public function getModules(): Collection
    {
        return $this->modules->sortByDesc(
            fn(TelegramModuleInterface $module) => $module->getPriority()
        );
    }

    /**
     * Get a specific module by name
     */
    public function getModule(string $name): ?TelegramModuleInterface
    {
        return $this->modulesByName[$name] ?? null;
    }

    /**
     * Handle message through modules
     */
    public function handleMessage(TelegramMessage $message): ?TelegramResponse
    {
        foreach ($this->getEnabledModules() as $module) {
            try {
                $response = $module->handleMessage($message);
                if ($response && $response->isHandled()) {
                    Log::debug("Message handled by module: {$module->getModuleName()}");
                    return $response;
                }
            } catch (\Throwable $e) {
                Log::error("Module {$module->getModuleName()} failed to handle message", [
                    'error' => $e->getMessage(),
                    'module' => $module->getModuleName(),
                    'user_id' => $message->userId
                ]);
            }
        }

        return null;
    }

    /**
     * Handle callback through modules
     */
    public function handleCallback(TelegramCallback $callback): ?TelegramResponse
    {
        foreach ($this->getEnabledModules() as $module) {
            try {
                $response = $module->handleCallback($callback);
                if ($response && $response->isHandled()) {
                    Log::debug("Callback handled by module: {$module->getModuleName()}");
                    return $response;
                }
            } catch (\Throwable $e) {
                Log::error("Module {$module->getModuleName()} failed to handle callback", [
                    'error' => $e->getMessage(),
                    'module' => $module->getModuleName(),
                    'user_id' => $callback->userId
                ]);
            }
        }

        return null;
    }

    /**
     * Get enabled modules only
     */
    public function getEnabledModules(): Collection
    {
        return $this->getModules()->filter(
            fn(TelegramModuleInterface $module) => $module->isEnabled()
        );
    }

    /**
     * Get module status information
     */
    public function getModuleStatus(): array
    {
        return $this->modules->map(function (TelegramModuleInterface $module) {
            return [
                'name' => $module->getModuleName(),
                'version' => $module->getModuleVersion(),
                'enabled' => $module->isEnabled(),
                'priority' => $module->getPriority(),
                'dependencies' => $module->getDependencies(),
            ];
        })->toArray();
    }

    /**
     * Enable a module
     */
    public function enableModule(string $name): bool
    {
        $module = $this->getModule($name);
        if (!$module) {
            return false;
        }

        // This would typically set a database flag or config
        // For now, we'll assume modules are enabled by default
        Log::info("Module enabled: {$name}");
        return true;
    }

    /**
     * Disable a module
     */
    public function disableModule(string $name): bool
    {
        $module = $this->getModule($name);
        if (!$module) {
            return false;
        }

        // This would typically set a database flag or config
        Log::info("Module disabled: {$name}");
        return true;
    }

    /**
     * Validate module dependencies
     */
    private function validateDependencies(TelegramModuleInterface $module): void
    {
        $dependencies = $module->getDependencies();
        
        foreach ($dependencies as $dependency) {
            if (!isset($this->modulesByName[$dependency])) {
                throw new \RuntimeException(
                    "Module {$module->getModuleName()} requires dependency {$dependency} which is not registered"
                );
            }
        }
    }

    /**
     * Find modules that depend on the given module
     */
    private function findDependents(string $moduleName): array
    {
        $dependents = [];
        
        foreach ($this->modules as $module) {
            if (in_array($moduleName, $module->getDependencies())) {
                $dependents[] = $module->getModuleName();
            }
        }

        return $dependents;
    }

    /**
     * Install all registered modules
     */
    public function installAll(): void
    {
        foreach ($this->modules as $module) {
            try {
                $module->install();
                Log::info("Module installed: {$module->getModuleName()}");
            } catch (\Throwable $e) {
                Log::error("Failed to install module: {$module->getModuleName()}", [
                    'error' => $e->getMessage()
                ]);
            }
        }
    }
}