from typing import List
from os import listdir
from os.path import isdir, join, exists
from importlib import import_module
from .utils import pascalcase
import logging
from django.apps import AppConfig


logger = logging.getLogger(__name__)


def get_enabled_apps_list(plugin_dirs: List[str]) -> List[str]:
    """Returns a list of validated and dependency-resolved app names."""
    # Step 1: Config validation
    config_validated = []
    for plugin_dir in plugin_dirs:
        for plugin_name in listdir(plugin_dir):
            plugin_path = join(plugin_dir, plugin_name)
            if isdir(plugin_path):
                if exists(join(plugin_path, "DISABLED")):
                    continue
                try:
                    app_config = get_app_config(plugin_name)
                    if validate_app_config(app_config):
                        config_validated.append(plugin_name)
                except Exception as e:
                    logger.warning(f"Failed to load app config for {plugin_name}: {e}")
    # Step 2: Dependency resolution
    enabled_apps_list = resolve_dependencies(config_validated)
    return enabled_apps_list


def resolve_dependencies(validated_plugins: List[str]) -> List[str]:
    """Resolves dependencies and returns plugins in dependency order using topological sort."""
    result = []
    visiting = set()
    visited = set()

    def visit(plugin_name: str):
        if plugin_name in visited:
            return
        if plugin_name in visiting:
            raise ValueError(
                f"Circular dependency detected involving plugin: {plugin_name}"
            )
        visiting.add(plugin_name)
        config = get_app_config(plugin_name)
        dependencies = getattr(config, "dependencies", [])
        for dep in dependencies:
            if dep not in validated_plugins:
                logger.warning(
                    f"Plugin {plugin_name} depends on {dep} which is not available or validated"
                )
                visiting.remove(plugin_name)
                visited.add(plugin_name)
                return
            visit(dep)
        visiting.remove(plugin_name)
        visited.add(plugin_name)
        result.append(plugin_name)

    for plugin in validated_plugins:
        visit(plugin)

    return result


def get_app_config(f: str):
    """Load and return the AppConfig for a plugin."""
    return getattr(import_module(f + ".apps"), pascalcase(f[len("p_") :]) + "Config")


def validate_app_config(app_config: AppConfig) -> bool:
    """Validate that an app config has required fields based on its type."""
    if not hasattr(app_config, "p_type"):
        logger.warning(f"App config {app_config.name} is missing required field p_type")
        return False

    if app_config.p_type == "core":
        return True
    if app_config.p_type == "app":
        required_fields = ["name", "verbose_name", "url_prefix"]
    elif app_config.p_type == "plugin":
        required_fields = ["name"]
    elif app_config.p_type == "service":
        required_fields = []
    else:
        logger.warning(
            f"App config {app_config.name} has invalid p_type {app_config.p_type}"
        )
        return False

    for field in required_fields:
        if not hasattr(app_config, field):
            logger.warning(
                f"App config {app_config.name} is missing required field {field}"
            )
            return False

    return True
