"""
Dynamic Gradio Apps

Main application that dynamically loads apps from the apps/ directory
and renders them as tabs in a Gradio interface.
"""

import importlib
import inspect
from pathlib import Path
from typing import List, Tuple, Type

import gradio as gr

from apps.base_app import BaseApp
from config import settings
from utils.general import HuggingFaceManager
from utils.logger import get_console, get_logger, setup_logging

# Setup centralized logging
setup_logging(settings)
logger = get_logger(__name__)
console = get_console()


def discover_apps() -> List[Tuple[str, Type[BaseApp]]]:
    """
    Discover all app classes in the apps directory.

    Returns:
        List of tuples (app_key, app_class) for apps that inherit from BaseApp
    """
    apps_dir = Path(settings.apps_directory)
    discovered_apps: List[Tuple[str, Type[BaseApp]]] = []

    if not apps_dir.exists():
        logger.warning(f"[yellow]Apps directory '{apps_dir}' not found[/yellow]")
        return discovered_apps

    # Find all Python files in the apps directory
    for py_file in apps_dir.glob("*.py"):
        # Skip __init__.py and base_app.py
        if py_file.stem in ["__init__", "base_app"]:
            continue

        app_key = py_file.stem

        try:
            # Import the module
            module_name = f"{settings.apps_directory}.{app_key}"
            logger.info(f"[cyan]Loading module: {module_name}[/cyan]")
            module = importlib.import_module(module_name)

            # Look for the 'app' variable in the module
            if hasattr(module, "app"):
                app_class = module.app

                # Verify it's a class that inherits from BaseApp
                if (
                    inspect.isclass(app_class)
                    and issubclass(app_class, BaseApp)
                    and app_class != BaseApp
                ):
                    discovered_apps.append((app_key, app_class))
                    logger.info(
                        f"[green]✓[/green] Loaded app: [bold]{app_class.__name__}[/bold] (key: {app_key})"
                    )
                else:
                    logger.warning(
                        f"[yellow]Module {module_name} has 'app' but it's not a valid BaseApp subclass[/yellow]"
                    )
            else:
                logger.warning(
                    f"[yellow]Module {module_name} doesn't export 'app' variable[/yellow]"
                )

        except Exception as e:
            logger.error(f"[red]Error loading {py_file.name}: {e}[/red]", exc_info=True)

    return discovered_apps


def create_main_interface() -> gr.Blocks:
    """
    Create the main Gradio interface with tabs for each app.

    Returns:
        gr.Blocks: Main Gradio interface
    """
    # Discover all apps
    discovered_apps = discover_apps()

    if not discovered_apps:
        logger.warning(
            "[yellow]No apps found! Creating a placeholder interface.[/yellow]"
        )
        with gr.Blocks(title=settings.project_title) as demo:
            gr.Markdown("# No Apps Found")
            gr.Markdown(f"""
            No apps were found in the `{settings.apps_directory}` directory.

            To add apps:
            1. Create a Python file in the `{settings.apps_directory}` directory
            2. Create a class that inherits from `BaseApp`
            3. Implement the required method: `create_interface()`
            4. Export the class as `app` (e.g., `app = YourAppClass`)
            5. Add app configuration to `config.yaml` under `apps:` section

            See the existing example apps for reference.
            """)
        return demo

    # Filter enabled apps and sort by order
    enabled_apps = []
    for app_key, app_class in discovered_apps:
        # Create a temporary instance to check if enabled
        temp_instance = app_class(app_key=app_key)
        if temp_instance.enabled:
            enabled_apps.append((app_key, app_class, temp_instance.order))
        else:
            logger.info(f"[dim]App '{app_key}' is disabled in config[/dim]")

    # Sort by order
    enabled_apps.sort(key=lambda x: x[2])

    # Create the main interface with tabs
    with gr.Blocks(
        title=settings.project_title,
        theme=settings.theme if settings.theme != "default" else None,
    ) as demo:
        gr.Markdown(f"# {settings.project_title}")
        gr.Markdown(
            f"*{len(enabled_apps)} apps loaded from `{settings.apps_directory}/`*"
        )

        # Create a tab for each app
        with gr.Tabs():
            for app_key, app_class, _ in enabled_apps:
                try:
                    # Instantiate the app with settings and app_key
                    app_instance = app_class(app_key=app_key)

                    # Create a tab with the app's interface
                    with gr.Tab(app_instance.name):
                        if app_instance.description:
                            gr.Markdown(f"*{app_instance.description}*")
                        app_instance.create_interface()

                    logger.info(
                        f"[green]✓[/green] Created tab for: [bold]{app_instance.title}[/bold] (order: {app_instance.order})"
                    )

                except Exception as e:
                    logger.error(
                        f"[red]Error creating interface for {app_class.__name__}: {e}[/red]",
                        exc_info=True,
                    )
                    # Create an error tab
                    with gr.Tab(f"Error: {app_key}"):
                        gr.Markdown(f"## Error loading app\n\n```\n{str(e)}\n```")

    return demo


def main():
    """Main entry point for the application."""
    logger.info("[cyan]Creating Gradio demo instance...[/cyan]")
    hf_manager = HuggingFaceManager()
    hf_manager.sync_repositories()
    demo = create_main_interface()
    logger.info("[green]✓[/green] Gradio app created")
    return demo


# Create app instance for HuggingFace Spaces and other imports
app = main()


if __name__ == "__main__":
    # When run directly (not imported), the app is already created above
    pass
