"""
Configuration management for infra services
"""

import os
import json
from pathlib import Path
from typing import Dict, Optional, List


class Config:
    """Configuration management for infra services"""

    INSTALL_DIR = Path("/opt/infra-services")
    SERVICES_DIR = INSTALL_DIR / "services"
    CONFIG_DIR = Path("/etc/infra")
    ENV_FILE = CONFIG_DIR / "infra.env"

    # User config directory for credentials (personal, takes precedence)
    USER_CONFIG_DIR = Path.home() / ".config" / "infra-cli"
    USER_CLOUDFLARE_FILE = USER_CONFIG_DIR / "cloudflare.json"

    # System config directory for shared credentials (requires sudo to modify)
    SYSTEM_CONFIG_DIR = Path("/etc/infra-cli")
    SYSTEM_CLOUDFLARE_FILE = SYSTEM_CONFIG_DIR / "cloudflare.json"

    # Default services (for backward compatibility)
    SERVICES = ["traefik", "wireguard", "mqtt"]
    NETWORKS = ["infra-public", "infra-private"]

    @staticmethod
    def get_managed_services() -> List[str]:
        """Discover all managed stacks from the install directory

        Scans both infrastructure/ and services/ directories.
        Returns stack names like 'infrastructure/traefik/default'
        """
        services = []

        if not Config.INSTALL_DIR.exists():
            return services

        # Find all docker-compose files recursively
        for compose_file in Config.INSTALL_DIR.rglob("docker-compose*.yml"):
            # Skip if it's in a hidden directory
            if any(part.startswith('.') for part in compose_file.parts):
                continue
            # Skip disabled files
            if compose_file.name.endswith('.disabled'):
                continue

            # Get the relative path from INSTALL_DIR
            rel_path = compose_file.relative_to(Config.INSTALL_DIR)

            # Remove the docker-compose*.yml filename to get service path
            service_path = rel_path.parent

            # Use the full path as service name (e.g., "infrastructure/traefik/default")
            service_name = str(service_path)

            # Avoid duplicates
            if service_name and service_name not in services:
                services.append(service_name)

        return sorted(services)

    @staticmethod
    def get_service_compose_files(service_name: str) -> List[Path]:
        """Get all docker-compose files for a given service

        Args:
            service_name: Service name (e.g., "infrastructure/traefik/default")

        Returns:
            List of paths to docker-compose files for this service
        """
        service_dir = Config.INSTALL_DIR / service_name

        if not service_dir.exists():
            return []

        # Find all docker-compose*.yml files in the service directory
        compose_files = sorted(service_dir.glob("docker-compose*.yml"))

        return compose_files

    @staticmethod
    def load_env() -> Dict[str, str]:
        """Load environment variables from .env file"""
        env_vars = {}
        if Config.ENV_FILE.exists():
            with open(Config.ENV_FILE) as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith("#") and "=" in line:
                        key, value = line.split("=", 1)
                        env_vars[key.strip()] = value.strip()
        return env_vars

    @staticmethod
    def get_env(key: str, default: Optional[str] = None) -> Optional[str]:
        """Get a specific environment variable"""
        env_vars = Config.load_env()
        return env_vars.get(key, default)

    @staticmethod
    def set_env(key: str, value: str) -> bool:
        """Set an environment variable in .env file"""
        env_vars = Config.load_env()
        env_vars[key] = value

        try:
            Config.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
            with open(Config.ENV_FILE, "w") as f:
                f.write("# Infra Services Environment Variables\n")
                f.write("# Secrets file - permissions: 600\n\n")
                for k, v in env_vars.items():
                    f.write(f"{k}={v}\n")
            # Set restrictive permissions
            Config.ENV_FILE.chmod(0o600)
            return True
        except Exception as e:
            print(f"Error writing env file: {e}")
            return False

    @staticmethod
    def get_service_dir(service_name: str) -> Path:
        """Get the directory for a specific service"""
        return Config.SERVICES_DIR / service_name

    @staticmethod
    def service_exists(service_name: str) -> bool:
        """Check if a service exists (has any docker-compose files)"""
        compose_files = Config.get_service_compose_files(service_name)
        return len(compose_files) > 0

    @staticmethod
    def get_cloudflare_tokens() -> Dict[str, Dict[str, str]]:
        """Load all Cloudflare tokens from both user and system config.

        User tokens take precedence over system tokens with the same name.
        Each token dict includes 'source': 'user' or 'system'.
        """
        tokens = {}

        # Load system tokens first (lower precedence)
        if Config.SYSTEM_CLOUDFLARE_FILE.exists():
            try:
                with open(Config.SYSTEM_CLOUDFLARE_FILE) as f:
                    system_tokens = json.load(f)
                    for name, data in system_tokens.items():
                        data["source"] = "system"
                        tokens[name] = data
            except:
                pass

        # Load user tokens (override system tokens with same name)
        if Config.USER_CLOUDFLARE_FILE.exists():
            try:
                with open(Config.USER_CLOUDFLARE_FILE) as f:
                    user_tokens = json.load(f)
                    for name, data in user_tokens.items():
                        data["source"] = "user"
                        tokens[name] = data
            except:
                pass

        return tokens

    @staticmethod
    def add_cloudflare_token(name: str, token: str, system: bool = False) -> bool:
        """Add a named Cloudflare token.

        Args:
            name: Friendly name for the token
            token: The Cloudflare API token
            system: If True, save to system config (requires sudo). Default: user config.
        """
        from datetime import datetime
        import subprocess

        new_token = {
            "token": token,
            "created_at": datetime.now().isoformat()
        }

        if system:
            # System-wide config (requires sudo)
            config_dir = Config.SYSTEM_CONFIG_DIR
            config_file = Config.SYSTEM_CLOUDFLARE_FILE

            # Load existing system tokens
            existing = {}
            if config_file.exists():
                try:
                    with open(config_file) as f:
                        existing = json.load(f)
                except:
                    pass

            existing[name] = new_token

            try:
                # Use sudo to create directory and write file
                subprocess.run(["sudo", "mkdir", "-p", str(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(existing, f, indent=2)
                    temp_path = f.name

                subprocess.run(["sudo", "mv", temp_path, str(config_file)], check=True)
                subprocess.run(["sudo", "chmod", "644", str(config_file)], check=True)
                return True
            except Exception as e:
                print(f"Error saving system Cloudflare token: {e}")
                return False
        else:
            # User config
            config_dir = Config.USER_CONFIG_DIR
            config_file = Config.USER_CLOUDFLARE_FILE

            # Load existing user tokens only
            existing = {}
            if config_file.exists():
                try:
                    with open(config_file) as f:
                        existing = json.load(f)
                except:
                    pass

            existing[name] = new_token

            try:
                config_dir.mkdir(parents=True, exist_ok=True)
                with open(config_file, "w") as f:
                    json.dump(existing, f, indent=2)
                config_file.chmod(0o600)
                return True
            except Exception as e:
                print(f"Error saving user Cloudflare token: {e}")
                return False

    @staticmethod
    def remove_cloudflare_token(name: str) -> bool:
        """Remove a named Cloudflare token.

        Removes from the appropriate config file based on where it exists.
        User tokens are removed from user config, system tokens require sudo.
        """
        import subprocess

        # First, find where this token exists
        tokens = Config.get_cloudflare_tokens()
        if name not in tokens:
            return False

        source = tokens[name].get("source", "user")

        if source == "system":
            # Remove from system config (requires sudo)
            if not Config.SYSTEM_CLOUDFLARE_FILE.exists():
                return False

            try:
                with open(Config.SYSTEM_CLOUDFLARE_FILE) as f:
                    system_tokens = json.load(f)
            except:
                return False

            if name not in system_tokens:
                return False

            del system_tokens[name]

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

                subprocess.run(["sudo", "mv", temp_path, str(Config.SYSTEM_CLOUDFLARE_FILE)], check=True)
                return True
            except Exception as e:
                print(f"Error removing system Cloudflare token: {e}")
                return False
        else:
            # Remove from user config
            if not Config.USER_CLOUDFLARE_FILE.exists():
                return False

            try:
                with open(Config.USER_CLOUDFLARE_FILE) as f:
                    user_tokens = json.load(f)
            except:
                return False

            if name not in user_tokens:
                return False

            del user_tokens[name]

            try:
                with open(Config.USER_CLOUDFLARE_FILE, "w") as f:
                    json.dump(user_tokens, f, indent=2)
                return True
            except Exception as e:
                print(f"Error removing user Cloudflare token: {e}")
                return False

    @staticmethod
    def get_cloudflare_token(name: str) -> Optional[str]:
        """Get a specific Cloudflare token by name"""
        tokens = Config.get_cloudflare_tokens()
        if name in tokens:
            return tokens[name]["token"]
        return None

    @staticmethod
    def get_service_cloudflare_token(service_name: str) -> Optional[str]:
        """Get the Cloudflare token configured for a specific service"""
        # Check service-specific config
        service_config = Config.get_service_config(service_name)
        if service_config and "cloudflare_token" in service_config:
            token_name = service_config["cloudflare_token"]
            return Config.get_cloudflare_token(token_name)

        # Fall back to default token
        return Config.get_env("CLOUDFLARE_API_TOKEN")

    @staticmethod
    def get_service_config(service_name: str) -> Optional[Dict]:
        """Get service-specific configuration"""
        config_file = Config.get_service_dir(service_name) / "service.json"
        if config_file.exists():
            try:
                with open(config_file) as f:
                    return json.load(f)
            except:
                pass
        return None

    @staticmethod
    def set_service_config(service_name: str, config: Dict) -> bool:
        """Set service-specific configuration"""
        config_file = Config.get_service_dir(service_name) / "service.json"
        try:
            with open(config_file, "w") as f:
                json.dump(config, f, indent=2)
            return True
        except Exception as e:
            print(f"Error writing service config: {e}")
            return False
