"""
ttyd manager for web-based TUI access
"""

import os
import json
import hashlib
import secrets
import subprocess
from pathlib import Path
from typing import Dict, Optional
from .config import Config


def copy_to_clipboard(text: str) -> Dict:
    """Copy text to clipboard, supporting OSC 52, Wayland, and X11.

    Args:
        text: Text to copy to clipboard

    Returns:
        Dict with success status and message
    """
    import base64
    import sys

    # Try OSC 52 escape sequence (works in most modern terminals, including over SSH)
    # This tells the terminal emulator to copy to system clipboard
    try:
        encoded = base64.b64encode(text.encode()).decode()
        # OSC 52 ; c ; <base64 data> ST
        osc52 = f"\033]52;c;{encoded}\a"
        sys.stdout.write(osc52)
        sys.stdout.flush()
        return {"success": True, "message": "Copied"}
    except Exception:
        pass

    # Try wl-copy (Wayland)
    try:
        result = subprocess.run(
            ["wl-copy", text],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return {"success": True, "message": "Copied (Wayland)"}
    except FileNotFoundError:
        pass

    # Try xclip (X11)
    try:
        result = subprocess.run(
            ["xclip", "-selection", "clipboard"],
            input=text,
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return {"success": True, "message": "Copied (X11)"}
    except FileNotFoundError:
        pass

    # Try xsel (X11 alternative)
    try:
        result = subprocess.run(
            ["xsel", "--clipboard", "--input"],
            input=text,
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return {"success": True, "message": "Copied (X11)"}
    except FileNotFoundError:
        pass

    return {"success": False, "message": "No clipboard method available"}


class TtydManager:
    """Manages ttyd service for web-based TUI access"""

    CONFIG_FILE = Config.SYSTEM_CONFIG_DIR / "ttyd.json"
    SERVICE_NAME = "infra-tui-web"
    SERVICE_FILE = Path(f"/etc/systemd/system/{SERVICE_NAME}.service")
    DEFAULT_PORT = 7681

    @staticmethod
    def _hash_password(password: str, salt: Optional[str] = None) -> Dict[str, str]:
        """Hash a password with salt using SHA-256.

        Args:
            password: Plain text password
            salt: Optional salt (generated if not provided)

        Returns:
            Dict with 'hash' and 'salt'
        """
        if salt is None:
            salt = secrets.token_hex(16)
        password_hash = hashlib.sha256(f"{salt}{password}".encode()).hexdigest()
        return {"hash": password_hash, "salt": salt}

    @staticmethod
    def _verify_password(password: str, stored_hash: str, salt: str) -> bool:
        """Verify a password against stored hash.

        Args:
            password: Plain text password to verify
            stored_hash: Stored password hash
            salt: Stored salt

        Returns:
            True if password matches
        """
        computed = hashlib.sha256(f"{salt}{password}".encode()).hexdigest()
        return computed == stored_hash

    @staticmethod
    def is_installed() -> bool:
        """Check if ttyd is installed"""
        try:
            result = subprocess.run(
                ["which", "ttyd"],
                capture_output=True,
                text=True
            )
            return result.returncode == 0
        except Exception:
            return False

    @staticmethod
    def get_version() -> Optional[str]:
        """Get ttyd version if installed"""
        try:
            result = subprocess.run(
                ["ttyd", "--version"],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                # Output is like "ttyd version 1.7.4"
                return result.stdout.strip().split()[-1]
            return None
        except Exception:
            return None

    @staticmethod
    def install() -> Dict:
        """Install ttyd using apt

        Returns:
            Dict with success status and message
        """
        try:
            # Try apt install first
            result = subprocess.run(
                ["sudo", "apt", "install", "-y", "ttyd"],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return {"success": True, "message": "ttyd installed successfully"}
            else:
                return {"success": False, "message": f"apt install failed: {result.stderr}"}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def get_config() -> Dict:
        """Load ttyd configuration

        Returns:
            Dict with port, username, password_hash, salt, enabled_on_boot
        """
        default_config = {
            "port": TtydManager.DEFAULT_PORT,
            "username": "",
            "password_hash": "",
            "password_salt": "",
            "enabled_on_boot": False
        }

        if not TtydManager.CONFIG_FILE.exists():
            return default_config

        try:
            with open(TtydManager.CONFIG_FILE) as f:
                config = json.load(f)
                # Merge with defaults
                return {**default_config, **config}
        except Exception:
            return default_config

    @staticmethod
    def save_config(port: int, username: str, password: str, enabled_on_boot: bool = False) -> Dict:
        """Save ttyd configuration with hashed password

        Args:
            port: Port number for ttyd
            username: Username for basic auth
            password: Plain text password (will be hashed)
            enabled_on_boot: Whether to enable service on boot

        Returns:
            Dict with success status and message
        """
        try:
            # Hash the password
            pw_data = TtydManager._hash_password(password)

            config = {
                "port": port,
                "username": username,
                "password_hash": pw_data["hash"],
                "password_salt": pw_data["salt"],
                "enabled_on_boot": enabled_on_boot
            }

            # Ensure config directory exists
            subprocess.run(["sudo", "mkdir", "-p", str(Config.SYSTEM_CONFIG_DIR)], check=True)

            # Write to temp file then sudo move
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
                json.dump(config, f, indent=2)
                temp_path = f.name

            subprocess.run(["sudo", "mv", temp_path, str(TtydManager.CONFIG_FILE)], check=True)
            subprocess.run(["sudo", "chmod", "600", str(TtydManager.CONFIG_FILE)], check=True)

            return {"success": True, "message": "Configuration saved"}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def get_plain_password_for_service() -> Optional[str]:
        """Get the password in a form usable by ttyd.

        Since ttyd requires plain text credentials, we store a separate
        encrypted credential file that only root can read.

        Note: This is a limitation of ttyd - it doesn't support hashed passwords.
        We store the hash for verification purposes, but also need the plain password
        for the systemd service. The config file is chmod 600 and owned by root.

        Returns:
            Plain password if available, None otherwise
        """
        cred_file = Config.SYSTEM_CONFIG_DIR / "ttyd_cred"
        if not cred_file.exists():
            return None
        try:
            result = subprocess.run(
                ["sudo", "cat", str(cred_file)],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return result.stdout.strip()
            return None
        except Exception:
            return None

    @staticmethod
    def save_config_with_credential(port: int, username: str, password: str, enabled_on_boot: bool = False) -> Dict:
        """Save ttyd configuration and store credential securely

        Args:
            port: Port number for ttyd
            username: Username for basic auth
            password: Plain text password
            enabled_on_boot: Whether to enable service on boot

        Returns:
            Dict with success status and message
        """
        try:
            # Hash the password for verification purposes
            pw_data = TtydManager._hash_password(password)

            config = {
                "port": port,
                "username": username,
                "password_hash": pw_data["hash"],
                "password_salt": pw_data["salt"],
                "enabled_on_boot": enabled_on_boot
            }

            # Ensure config directory exists
            subprocess.run(["sudo", "mkdir", "-p", str(Config.SYSTEM_CONFIG_DIR)], check=True)

            # Write config file
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
                json.dump(config, f, indent=2)
                temp_path = f.name

            subprocess.run(["sudo", "mv", temp_path, str(TtydManager.CONFIG_FILE)], check=True)
            subprocess.run(["sudo", "chmod", "600", str(TtydManager.CONFIG_FILE)], check=True)

            # Store credential securely (root only) for systemd service
            cred_file = Config.SYSTEM_CONFIG_DIR / "ttyd_cred"
            with tempfile.NamedTemporaryFile(mode='w', suffix='.cred', delete=False) as f:
                f.write(password)
                cred_temp = f.name

            subprocess.run(["sudo", "mv", cred_temp, str(cred_file)], check=True)
            subprocess.run(["sudo", "chmod", "600", str(cred_file)], check=True)

            return {"success": True, "message": "Configuration saved"}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def create_systemd_service() -> Dict:
        """Create systemd service file for ttyd

        Returns:
            Dict with success status and message
        """
        config = TtydManager.get_config()
        username = config.get("username", "")
        port = config.get("port", TtydManager.DEFAULT_PORT)

        # Get password from secure credential file
        password = TtydManager.get_plain_password_for_service()

        if not username or not password:
            return {"success": False, "message": "Username and password must be configured first"}

        # Find infra-tui command path
        try:
            result = subprocess.run(["which", "infra-tui"], capture_output=True, text=True)
            if result.returncode != 0:
                return {"success": False, "message": "infra-tui command not found"}
            infra_tui_path = result.stdout.strip()
        except Exception as e:
            return {"success": False, "message": f"Could not find infra-tui: {e}"}

        # Find ttyd path
        try:
            result = subprocess.run(["which", "ttyd"], capture_output=True, text=True)
            if result.returncode != 0:
                return {"success": False, "message": "ttyd not installed"}
            ttyd_path = result.stdout.strip()
        except Exception as e:
            return {"success": False, "message": f"Could not find ttyd: {e}"}

        service_content = f"""[Unit]
Description=Infra TUI Web Access (ttyd)
After=network.target

[Service]
Type=simple
ExecStart={ttyd_path} -W -p {port} -c {username}:{password} {infra_tui_path}
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
"""

        try:
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', suffix='.service', delete=False) as f:
                f.write(service_content)
                temp_path = f.name

            subprocess.run(["sudo", "mv", temp_path, str(TtydManager.SERVICE_FILE)], check=True)
            subprocess.run(["sudo", "chmod", "644", str(TtydManager.SERVICE_FILE)], check=True)
            subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)

            return {"success": True, "message": "Systemd service created"}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def get_service_status() -> Dict:
        """Get ttyd service status

        Returns:
            Dict with status info: running, enabled, active_state
        """
        try:
            # Check if service is active
            result = subprocess.run(
                ["systemctl", "is-active", TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            is_running = result.stdout.strip() == "active"

            # Check if service is enabled
            result = subprocess.run(
                ["systemctl", "is-enabled", TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            is_enabled = result.stdout.strip() == "enabled"

            # Get detailed status
            result = subprocess.run(
                ["systemctl", "show", TtydManager.SERVICE_NAME, "--property=ActiveState,SubState"],
                capture_output=True,
                text=True
            )
            state_info = {}
            for line in result.stdout.strip().split('\n'):
                if '=' in line:
                    key, value = line.split('=', 1)
                    state_info[key] = value

            return {
                "running": is_running,
                "enabled": is_enabled,
                "active_state": state_info.get("ActiveState", "unknown"),
                "sub_state": state_info.get("SubState", "unknown"),
                "service_exists": TtydManager.SERVICE_FILE.exists()
            }
        except Exception:
            return {
                "running": False,
                "enabled": False,
                "active_state": "unknown",
                "sub_state": "unknown",
                "service_exists": False
            }

    @staticmethod
    def start_service() -> Dict:
        """Start the ttyd service

        Returns:
            Dict with success status and message
        """
        # Check if service exists, create if needed
        if not TtydManager.SERVICE_FILE.exists():
            result = TtydManager.create_systemd_service()
            if not result["success"]:
                return result

        try:
            result = subprocess.run(
                ["sudo", "systemctl", "start", TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return {"success": True, "message": "Service started"}
            else:
                return {"success": False, "message": result.stderr}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def stop_service() -> Dict:
        """Stop the ttyd service

        Returns:
            Dict with success status and message
        """
        try:
            result = subprocess.run(
                ["sudo", "systemctl", "stop", TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return {"success": True, "message": "Service stopped"}
            else:
                return {"success": False, "message": result.stderr}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def restart_service() -> Dict:
        """Restart the ttyd service

        Returns:
            Dict with success status and message
        """
        # Recreate service file to pick up any config changes
        result = TtydManager.create_systemd_service()
        if not result["success"]:
            return result

        try:
            result = subprocess.run(
                ["sudo", "systemctl", "restart", TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return {"success": True, "message": "Service restarted"}
            else:
                return {"success": False, "message": result.stderr}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def enable_on_boot(enable: bool = True) -> Dict:
        """Enable or disable service on boot

        Args:
            enable: True to enable, False to disable

        Returns:
            Dict with success status and message
        """
        action = "enable" if enable else "disable"
        try:
            result = subprocess.run(
                ["sudo", "systemctl", action, TtydManager.SERVICE_NAME],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                return {"success": True, "message": f"Service {action}d on boot"}
            else:
                return {"success": False, "message": result.stderr}
        except Exception as e:
            return {"success": False, "message": str(e)}

    @staticmethod
    def get_public_ip() -> Optional[str]:
        """Get the server's public IP address.

        Reuses the same approach as CloudflareManager.
        """
        import requests
        try:
            services = [
                "https://api.ipify.org",
                "https://ifconfig.me/ip",
                "https://icanhazip.com",
            ]
            for url in services:
                try:
                    response = requests.get(url, timeout=5)
                    if response.status_code == 200:
                        return response.text.strip()
                except Exception:
                    continue
            return None
        except Exception:
            return None

    @staticmethod
    def get_local_ip() -> Optional[str]:
        """Get the server's local IP address."""
        try:
            result = subprocess.run(["hostname", "-I"], capture_output=True, text=True)
            if result.returncode == 0:
                return result.stdout.strip().split()[0]
        except Exception:
            pass
        return None

    @staticmethod
    def get_access_url(use_public_ip: bool = True) -> Optional[str]:
        """Get the URL to access the web TUI

        Args:
            use_public_ip: If True, try to get public IP first

        Returns:
            URL string or None if not configured
        """
        config = TtydManager.get_config()
        port = config.get("port", TtydManager.DEFAULT_PORT)

        ip = None
        if use_public_ip:
            ip = TtydManager.get_public_ip()

        if not ip:
            ip = TtydManager.get_local_ip()

        if ip:
            return f"http://{ip}:{port}"

        return f"http://localhost:{port}"
