import click
import os
from typing import Optional, List, Tuple, Any
import docker
from docker.client import DockerClient
import time
from textual.app import App, ComposeResult
from textual.containers import Container, Horizontal, Vertical, Grid
from textual.widgets import Button, Input, Select, Label, Header, Footer, Static, Pretty
from textual.screen import Screen
from textual.binding import Binding
from textual import events
from textual.widgets import DataTable
from rich.text import Text

from ..notifications import notification_manager, EventType
from ..databases import (
    get_database_config, 
    DATABASES, 
    DatabaseCredentials,
    credentials_manager
)
from ..style import (
    print_success,
    print_error,
    print_warning,
    print_action,
    create_loading_status,
    create_progress,
    get_db_icon
)
from ..functions import (
    get_container_name,
    find_container,
    get_db_info,
    get_host_ip,
    get_connection_string,
    get_cli_command,
    generate_password)
from ..functions.docker_utils import (
    find_next_available_port,
    check_container_exists,
    remove_container_if_exists,
)

class SummaryPanel(Static):
    """Shows a summary of the database configuration."""
    
    def __init__(self):
        super().__init__()
        self.styles.background = "rgb(20,20,20)"
        self.styles.border = ("heavy", "blue")
        self.styles.padding = (1, 2)
        
    def update_summary(self, db_name, db_type, version, access, port, webhook, force):
        """Update the summary panel with current values."""
        content = Text()
        content.append("📝 Configuration Summary\n\n", style="bold blue")
        
        if db_name:
            content.append("Name: ", style="cyan")
            content.append(f"{db_name}\n", style="white")
        
        if db_type:
            content.append("Type: ", style="cyan")
            content.append(f"{get_db_icon(db_type)} {DATABASES[db_type].name}\n", style="white")
        
        if version:
            content.append("Version: ", style="cyan")
            content.append(f"{version}\n", style="white")
        else:
            content.append("Version: ", style="cyan")
            content.append("Latest\n", style="white")
        
        content.append("Access: ", style="cyan")
        content.append(f"{'🌍 Public' if access == 'public' else '🔒 Private'}\n", style="white")
        
        content.append("Port: ", style="cyan")
        content.append(f"{port or 'Default'}\n", style="white")
        
        if webhook:
            content.append("Discord: ", style="cyan")
            content.append("✅ Configured\n", style="green")
        
        if force:
            content.append("\n⚠️ Force mode enabled", style="yellow bold")
        
        self.update(content)

class DatabaseCreationScreen(Screen):
    BINDINGS = [
        Binding("escape", "quit", "Quit", show=True),
        Binding("enter", "submit", "Create Database", show=True),
        Binding("f1", "toggle_help", "Help", show=True)
    ]
    
    def __init__(self, callback):
        super().__init__()
        self.callback = callback
        self.db_name = ""
        self.db_type = "postgres"
        self.version = None
        self.access = "public"
        self.port = None
        self.discord_webhook = None
        self.force = False

    def compose(self) -> ComposeResult:
        """Create child widgets for the screen."""
        yield Header(show_clock=True)
        
        with Grid(id="main-grid"):
            with Vertical(id="form-container"):
                yield Static("🎯 Create New Database", id="title")
                
                with Vertical(id="form-scroll"):
                    with Vertical(id="form"):
                        yield Label("Database Name:", classes="label")
                        yield Input(placeholder="Enter database name", id="db_name")
                        
                        yield Label("Database Type:", classes="label")
                        yield Select(
                            [(f"{get_db_icon(db)} {DATABASES[db].name}", db) for db in DATABASES],
                            id="db_type",
                            value="postgres"
                        )
                        
                        yield Label("Version:", classes="label")
                        yield Select(
                            [("Latest", None)] + [(v, v) for v in DATABASES["postgres"].supported_versions],
                            id="version"
                        )
                        
                        yield Label("Access Type:", classes="label")
                        yield Select(
                            [("🌍 Public (accessible from anywhere)", "public"),
                             ("🔒 Private (localhost only)", "private")],
                            id="access",
                            value="public"
                        )
                        
                        yield Label("Port:", classes="label")
                        yield Input(placeholder="Leave empty for default", id="port")
                        
                        yield Label("Discord Webhook (optional):", classes="label")
                        yield Input(placeholder="Discord webhook URL", id="webhook")
                        
                        with Horizontal(id="buttons"):
                            yield Button("Force Overwrite", id="force", variant="default")
                            yield Button("Create Database", id="create", variant="primary")
                            yield Button("Cancel", id="cancel", variant="error")
            
            with Container(id="summary-container"):
                yield SummaryPanel()
        
        yield Footer()

    def on_mount(self) -> None:
        """Handle screen mount."""
        self.update_summary()

    def update_summary(self) -> None:
        """Update the summary panel."""
        summary = self.query_one(SummaryPanel)
        summary.update_summary(
            self.db_name,
            self.db_type,
            self.version,
            self.access,
            self.port,
            self.discord_webhook,
            self.force
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle button presses."""
        button_id = event.button.id
        if button_id == "force":
            self.force = not self.force
            event.button.variant = "warning" if self.force else "default"
            self.update_summary()
        elif button_id == "create":
            self.action_submit()
        elif button_id == "cancel":
            self.action_quit()

    def on_input_changed(self, event: Input.Changed) -> None:
        """Handle input changes."""
        if event.input.id == "db_name":
            self.db_name = event.value
        elif event.input.id == "port":
            try:
                self.port = int(event.value) if event.value else None
            except ValueError:
                pass
        elif event.input.id == "webhook":
            self.discord_webhook = event.value if event.value else None
        self.update_summary()

    def on_select_changed(self, event: Select.Changed) -> None:
        """Handle select changes."""
        if event.select.id == "db_type":
            self.db_type = event.value
            # Update version options based on selected database
            version_select = self.query_one("#version", Select)
            versions = DATABASES[self.db_type].supported_versions
            version_select.set_options([("Latest", None)] + [(v, v) for v in versions])
        elif event.select.id == "version":
            self.version = event.value
        elif event.select.id == "access":
            self.access = event.value
        self.update_summary()

    def action_toggle_help(self) -> None:
        """Show help information."""
        self.notify(
            "💡 Keyboard Shortcuts:\n"
            "• Tab/Shift+Tab: Navigate fields\n"
            "• Enter: Create database\n"
            "• Escape: Cancel\n"
            "• F1: Show this help",
            title="Help"
        )

    def action_submit(self) -> None:
        """Handle form submission."""
        if not self.db_name:
            self.notify("Database name is required", severity="error")
            return
            
        if self.port and (self.port < 1024 or self.port > 65535):
            self.notify("Port must be between 1024 and 65535", severity="error")
            return
            
        if self.discord_webhook and not self.discord_webhook.startswith("https://discord.com/api/webhooks/"):
            self.notify("Invalid Discord webhook URL", severity="error")
            return
            
        self.callback(
            db_name=self.db_name,
            db_type=self.db_type,
            version=self.version,
            access=self.access,
            port=self.port,
            discord_webhook=self.discord_webhook,
            force=self.force
        )
        self.app.exit()

    def action_quit(self) -> None:
        """Quit the application."""
        self.app.exit()

class DatabaseCreationApp(App):
    CSS = """
    Screen {
        align: center middle;
    }

    #main-grid {
        layout: grid;
        grid-size: 2;
        grid-columns: 3fr 1fr;
        height: 90%;
        margin: 1 2;
        background: $surface;
    }
    
    #form-container {
        height: 100%;
        border: heavy $accent;
        background: $boost;
    }
    
    #form-scroll {
        height: auto;
        overflow-y: scroll;
        width: 100%;
        border-bottom: heavy $accent;
    }
    
    #form {
        height: auto;
        width: 100%;
        padding: 0 2;
    }
    
    #summary-container {
        height: 100%;
        margin-left: 1;
    }
    
    #title {
        width: 100%;
        text-align: center;
        padding: 1;
        text-style: bold;
        color: $accent;
        background: $boost;
        border-bottom: heavy $accent;
    }
    
    .label {
        padding-top: 1;
        color: $text;
        margin-bottom: 1;
    }
    
    Input {
        margin-bottom: 1;
        border: tall $primary-darken-2;
    }
    
    Input:focus {
        border: tall $accent;
    }
    
    Select {
        margin-bottom: 1;
        border: tall $primary-darken-2;
    }
    
    Select:focus {
        border: tall $accent;
    }
    
    #buttons {
        margin-top: 2;
        margin-bottom: 2;
        align: center middle;
        width: 100%;
    }
    
    Button {
        margin: 1 2;
        min-width: 16;
        height: 3;
    }
    """
    
    def __init__(self, callback):
        super().__init__()
        self.callback = callback

    def on_mount(self) -> None:
        """Handle app mount."""
        self.push_screen(DatabaseCreationScreen(self.callback))

@click.command(name='create')
@click.argument('db_name', required=False)
@click.option('--type', 'db_type', type=click.Choice(list(DATABASES.keys())), help='Database type')
@click.option('--version', help='Database version')
@click.option('--access', type=click.Choice(['public', 'private']), help='Access type')
@click.option('--user', help='Database user to create')
@click.option('--port', type=int, help='Port to expose the database on')
@click.option('--discord-webhook', help='Discord webhook URL for notifications')
@click.option('--force', is_flag=True, help='Force creation by removing existing container if it exists')
def create_cmd(db_name: Optional[str], db_type: Optional[str], version: Optional[str], access: Optional[str], 
          user: Optional[str], port: Optional[int], discord_webhook: Optional[str], force: bool = False):
    """Create a new database."""
    
    def create_database(db_name, db_type, version, access, port, discord_webhook, force):
        nonlocal user
        try:
            client: DockerClient = docker.from_env()
            
            with create_loading_status(f"🔍 Checking configuration for {get_db_icon(db_type)} {db_type} database...") as status:
                # Get database configuration
                db_config = get_database_config(db_type, version)
                
                # Check if container already exists
                container_name = get_container_name(db_type, db_name)
                if check_container_exists(client, container_name):
                    if not force:
                        raise Exception(
                            f"A database named '{db_name}' already exists. Use --force to remove it and create a new one, "
                            f"or use a different name."
                        )
                    status.update(f"🗑️ Removing existing database '{db_name}' (--force flag used)")
                    remove_container_if_exists(client, container_name)
                
                # Use db_name as user if not specified
                if not user:
                    user = db_name
                    
                # Handle port allocation
                status.update("🔍 Finding available port...")
                if not port:
                    port = db_config.default_port
                
                # If port is taken, find next available port
                if not find_next_available_port(port, max_attempts=1):
                    next_port = find_next_available_port(port + 1, max_attempts=100)
                    if next_port:
                        print_warning(f"Port {port} is already in use. Using port {next_port} instead.")
                        port = next_port
                    else:
                        raise Exception(f"Could not find an available port starting from {port}")
                
                # Generate secure password
                status.update("🔑 Generating secure credentials...")
                password = generate_password()
                
                # Prepare environment variables
                environment = db_config.get_env_vars(db_name, user, password)
                
                # Configure ports
                ports = {f'{db_config.default_port}/tcp': port}
                
                # Configure network mode based on access type
                network_mode = 'bridge' if access == 'public' else 'host'
                
                # Prepare container configuration
                container_config = {
                    'image': db_config.image,
                    'name': container_name,
                    'environment': environment,
                    'ports': ports,
                    'network_mode': network_mode,
                    'detach': True
                }
                
                # Add optional configurations
                if db_config.volumes:
                    container_config['volumes'] = db_config.volumes
                if db_config.command:
                    container_config['command'] = db_config.command

            # Create progress bar for container creation
            progress = create_progress()
            with progress:
                task = progress.add_task(f"🚀 Creating {db_type} database...", total=100)
                
                # Pull image (30%)
                progress.update(task, advance=10, description=f"📥 Pulling {db_type} image...")
                try:
                    client.images.pull(db_config.image)
                except Exception as e:
                    print_warning(f"Could not pull latest image: {str(e)}")
                progress.update(task, advance=20)
                
                # Create and start container (40%)
                progress.update(task, advance=10, description="🏗️ Creating container...")
                container = client.containers.run(**container_config)
                progress.update(task, advance=30)
                
                # Wait for container to be healthy (30%)
                progress.update(task, description="🏥 Waiting for database to be healthy...")
                time.sleep(3)  # Give it a moment to start
                container.reload()  # Refresh container state
                
                if container.status != 'running':
                    raise Exception(f"Container failed to start. Logs:\n{container.logs().decode()}")
                
                progress.update(task, advance=30, description="✨ Finalizing setup...")
                
                # Get the appropriate host
                host = get_host_ip() if access == 'public' else 'localhost'
                
                # Store credentials with webhook URL
                creds = DatabaseCredentials(
                    db_type=db_type,
                    version=version,
                    user=user,
                    password=password,
                    port=port,
                    host=host,
                    access=access,
                    name=db_name,
                    webhook_url=discord_webhook
                )
                credentials_manager.store_credentials(creds)
                
                # Generate connection details
                conn_string = get_connection_string(db_type, host, port, user, password, db_name)
                cli_command = get_cli_command(db_type, host, port, user, password, db_name)
                
                # Complete progress
                progress.update(task, completed=100, description="✅ Database created successfully!")
            
            print_success(f"""
Database '{db_name}' created successfully!
Type: {db_config.name}
Version: {version or 'latest'}
Access: {access.upper()}
Host: {host}
Port: {port}
User: {user}
Password: {password}

Connection String (copy/paste ready):
{conn_string}

CLI Command:
{cli_command}

Tip: Use 'clidb info {db_name}' to see these details again.
""")
            
            # Send notification
            notification_manager.send_notification(
                event_type=EventType.DB_CREATED,
                db_info={
                    "name": db_name,
                    "type": db_config.name,
                    "version": version,
                    "host": host,
                    "port": port,
                    "access": access
                },
                webhook_url=discord_webhook
            )
                
        except Exception as e:
            print_error(f"Failed to create database: {str(e)}")
            notification_manager.send_notification(
                event_type=EventType.DB_CREATION_FAILED,
                db_info={"name": db_name, "type": db_type},
                webhook_url=discord_webhook,
                error_message=str(e)
            )

    # If no arguments provided, launch the TUI
    if not any([db_name, db_type, version, access, user, port, discord_webhook, force]):
        app = DatabaseCreationApp(create_database)
        app.run()
    else:
        # Set defaults for non-interactive mode
        db_type = db_type or 'postgres'
        access = access or 'public'
        create_database(db_name, db_type, version, access, port, discord_webhook, force)

@click.command(name='stop')
@click.argument('db_name')
def stop_cmd(db_name: str):
    """Stop a database."""
    try:
        client = docker.from_env()
        container = find_container(client, db_name)
        
        if not container:
            raise Exception(f"Database '{db_name}' not found")
        
        # Get credentials for webhook URL
        creds = credentials_manager.get_credentials(db_name)
        
        container.stop()
        print_action("Stop", db_name)
        
        if creds:
            # Get database info for notification
            db_name, db_type = get_db_info(container.name)
            notification_manager.send_notification(
                event_type=EventType.DB_STOPPED,
                db_info={"name": db_name, "type": db_type},
                webhook_url=creds.webhook_url
            )
    except Exception as e:
        print_action("Stop", db_name, success=False)
        print_error(str(e))
        if creds:
            notification_manager.send_notification(
                event_type=EventType.DB_STOP_FAILED,
                db_info={"name": db_name},
                webhook_url=creds.webhook_url,
                error_message=str(e)
            )

@click.command(name='start')
@click.argument('db_name')
def start_cmd(db_name: str):
    """Start a stopped database.
    
    Example: clidb start mydb
    """
    try:
        with create_loading_status(f"🔍 Finding database '{db_name}'...") as status:
            client = docker.from_env()
            container = find_container(client, db_name)
            
            if not container:
                raise Exception(f"Database '{db_name}' not found")
            
            # Get credentials for webhook URL
            creds = credentials_manager.get_credentials(db_name)
            
            status.update(f"🚀 Starting database '{db_name}'...")
            # Try to start the container
            container.start()
            
            # Wait a bit and check if it's actually running
            time.sleep(5)  # Give it time to start
            container.reload()  # Refresh container state
            
            if container.status != 'running':
                # Get the logs to see what went wrong
                logs = container.logs(tail=50).decode()
                raise Exception(f"Container failed to start. Logs:\n{logs}")
            
            status.update(f"✅ Database '{db_name}' started successfully!")
        
        print_action("Start", db_name)
        
        if creds:
            # Get database info for notification
            db_name, db_type = get_db_info(container.name)
            notification_manager.send_notification(
                event_type=EventType.DB_STARTED,
                db_info={"name": db_name, "type": db_type},
                webhook_url=creds.webhook_url
            )
        
    except Exception as e:
        print_error(f"Failed to start database: {str(e)}")
        if creds:
            notification_manager.send_notification(
                event_type=EventType.DB_START_FAILED,
                db_info={"name": db_name},
                webhook_url=creds.webhook_url,
                error_message=str(e)
            )

@click.command(name='remove')
@click.argument('db_name')
def remove_cmd(db_name: str):
    """Remove a database completely."""
    try:
        with create_loading_status(f"🔍 Finding database '{db_name}'...") as status:
            client = docker.from_env()
            container = find_container(client, db_name)
            
            if not container:
                raise Exception(f"Database '{db_name}' not found")
            
            # Get credentials for webhook URL before removal
            creds = credentials_manager.get_credentials(db_name)
            webhook_url = creds.webhook_url if creds else None
            
            # Get database info before removal for notification
            db_name, db_type = get_db_info(container.name)
            
            status.update(f"🗑️ Removing database '{db_name}'...")
            container.remove(force=True)
            
            status.update("🧹 Cleaning up credentials...")
            credentials_manager.remove_credentials(db_name)
        
        print_action("Remove", db_name)
        
        if webhook_url:
            notification_manager.send_notification(
                event_type=EventType.DB_REMOVED,
                db_info={"name": db_name, "type": db_type},
                webhook_url=webhook_url
            )
    except Exception as e:
        print_action("Remove", db_name, success=False)
        print_error(str(e))
        if webhook_url:
            notification_manager.send_notification(
                event_type=EventType.DB_REMOVE_FAILED,
                db_info={"name": db_name},
                webhook_url=webhook_url,
                error_message=str(e)
            )


@click.command(name='recreate')
@click.argument('db_name')
def recreate_cmd(db_name: str):
    """Recreate a database container from its stored configuration.
    
    Example: clidb recreate mydb
    """
    try:
        client = docker.from_env()
        container = find_container(client, db_name)
        creds = credentials_manager.get_credentials(db_name)
        
        if not container:
            raise Exception(f"Database '{db_name}' not found")
            
        if not creds:
            raise Exception(f"No credentials found for database '{db_name}'")
        
        # Get database configuration
        db_config = get_database_config(creds.db_type, creds.version)
        
        # Remove old container
        print_action("Removing", "old container")
        container.remove(force=True)
        
        # Prepare new container configuration
        container_config = {
            'image': db_config.image,
            'name': get_container_name(creds.db_type, db_name),
            'environment': db_config.get_env_vars(db_name, creds.user, creds.password),
            'ports': {f'{db_config.default_port}/tcp': creds.port},
            'network_mode': 'bridge' if creds.access == 'public' else 'host',
            'detach': True
        }
        
        # Add optional configurations
        if db_config.volumes:
            container_config['volumes'] = db_config.volumes
        if db_config.command:
            container_config['command'] = db_config.command
        
        # Create and start new container
        print_action("Creating", "new container")
        container = client.containers.run(**container_config)
        
        print_success(f"Database '{db_name}' recreated successfully")
            
    except Exception as e:
        print_error(f"Failed to recreate database: {str(e)}")

