"""
Merlya Commands - Host import/export utilities.

Import and export hosts from/to various file formats.
"""

from __future__ import annotations

import csv
import io
import json
import re
from pathlib import Path
from typing import TYPE_CHECKING, Any

from loguru import logger

from merlya.persistence.models import Host

if TYPE_CHECKING:
    from merlya.core.context import SharedContext

# Constants
DEFAULT_SSH_PORT = 22
MIN_PORT = 1
MAX_PORT = 65535
MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024  # 10MB
TAG_PATTERN = re.compile(r"^[a-zA-Z0-9_:-]{1,50}$")
ALLOWED_IMPORT_DIRS = [Path.home(), Path("/etc"), Path("/tmp")]


def validate_port(port_str: str, default: int = DEFAULT_SSH_PORT) -> int:
    """Validate and parse port number within valid bounds."""
    try:
        port = int(port_str)
        if MIN_PORT <= port <= MAX_PORT:
            return port
        logger.warning(f"⚠️ Port {port} out of range, using default {default}")
        return default
    except ValueError:
        return default


def validate_tag(tag: str) -> tuple[bool, str]:
    """Validate tag format. Returns (is_valid, error_message)."""
    if not tag:
        return False, "Tag cannot be empty"
    if not TAG_PATTERN.match(tag):
        return (
            False,
            f"Invalid tag format: '{tag}'. Use only letters, numbers, hyphens, underscores, and colons (max 50 chars)",
        )
    return True, ""


def validate_file_path(file_path: Path) -> tuple[bool, str]:
    """
    Validate file path for security (prevent path traversal attacks).

    Returns (is_valid, error_message).
    """
    try:
        resolved = file_path.resolve()
        is_allowed = any(
            resolved.is_relative_to(allowed.resolve()) for allowed in ALLOWED_IMPORT_DIRS
        )
        if not is_allowed:
            return False, "Access denied: Path must be within home directory, /etc, or /tmp"

        path_str = str(file_path)
        if ".." in path_str or path_str.startswith("/proc") or path_str.startswith("/sys"):
            return False, "Access denied: Invalid path pattern"

        return True, ""
    except Exception as e:
        return False, f"Invalid path: {e}"


def check_file_size(file_path: Path) -> tuple[bool, str]:
    """Check if file size is within limits. Returns (is_valid, error_message)."""
    try:
        size = file_path.stat().st_size
        if size > MAX_FILE_SIZE_BYTES:
            size_mb = size / (1024 * 1024)
            max_mb = MAX_FILE_SIZE_BYTES / (1024 * 1024)
            return False, f"File too large: {size_mb:.1f}MB (max: {max_mb:.0f}MB)"
        return True, ""
    except OSError as e:
        return False, f"Cannot read file: {e}"


def detect_import_format(file_path: Path, args: list[str]) -> str:
    """Detect file format from args or file extension."""
    for arg in args[1:]:
        if arg.startswith("--format="):
            return arg[9:].lower()

    if file_path.name == "hosts" and str(file_path).startswith("/etc"):
        return "etc_hosts"

    ext = file_path.suffix.lower()
    if ext in (".yml", ".yaml"):
        return "yaml"
    elif ext == ".csv":
        return "csv"
    elif ext == ".conf" or file_path.name == "config":
        return "ssh"
    return "json"


def detect_export_format(file_path: Path, args: list[str]) -> str:
    """Detect export format from args or file extension."""
    for arg in args[1:]:
        if arg.startswith("--format="):
            return arg[9:].lower()

    ext = file_path.suffix.lower()
    if ext in (".yml", ".yaml"):
        return "yaml"
    elif ext == ".csv":
        return "csv"
    return "json"


async def import_hosts(
    ctx: SharedContext,
    file_path: Path,
    file_format: str,
) -> tuple[int, list[str]]:
    """Import hosts from file. Returns (imported_count, errors)."""
    imported = 0
    errors: list[str] = []
    content = file_path.read_text()

    try:
        if file_format == "json":
            imported, errors = await _import_json(ctx, content)
        elif file_format == "yaml":
            imported, errors = await _import_yaml(ctx, content)
        elif file_format == "csv":
            imported, errors = await _import_csv(ctx, content)
        elif file_format == "ssh":
            imported, errors = await _import_ssh_config(ctx, file_path)
        elif file_format == "etc_hosts":
            imported, errors = await _import_etc_hosts(ctx, content)
    except Exception as e:
        logger.error(f"❌ Import failed: {e}")
        errors.append(str(e))

    return imported, errors


async def _import_json(ctx: SharedContext, content: str) -> tuple[int, list[str]]:
    """Import from JSON content."""
    imported = 0
    errors: list[str] = []
    data = json.loads(content)
    if not isinstance(data, list):
        data = [data]

    for item in data:
        try:
            host = create_host_from_dict(item)
            await ctx.hosts.create(host)
            imported += 1
        except Exception as e:
            errors.append(f"{item.get('name', '?')}: {e}")

    return imported, errors


async def _import_yaml(ctx: SharedContext, content: str) -> tuple[int, list[str]]:
    """Import from YAML content."""
    import yaml

    imported = 0
    errors: list[str] = []
    data = yaml.safe_load(content)
    if not isinstance(data, list):
        data = [data]

    for item in data:
        try:
            host = create_host_from_dict(item)
            await ctx.hosts.create(host)
            imported += 1
        except Exception as e:
            errors.append(f"{item.get('name', '?')}: {e}")

    return imported, errors


async def _import_csv(ctx: SharedContext, content: str) -> tuple[int, list[str]]:
    """Import from CSV content."""
    imported = 0
    errors: list[str] = []
    reader = csv.DictReader(io.StringIO(content))

    for row in reader:
        try:
            tags_raw = row.get("tags", "").split(",") if row.get("tags") else []
            valid_tags = [t.strip() for t in tags_raw if t.strip() and validate_tag(t.strip())[0]]
            host = Host(
                name=row["name"],
                hostname=row.get("hostname", row.get("host", row["name"])),
                port=validate_port(row.get("port", "22")),
                username=row.get("username", row.get("user")),
                tags=valid_tags,
            )
            await ctx.hosts.create(host)
            imported += 1
        except Exception as e:
            errors.append(f"{row.get('name', '?')}: {e}")

    return imported, errors


async def _import_ssh_config(ctx: SharedContext, file_path: Path) -> tuple[int, list[str]]:
    """Import from SSH config file."""
    from merlya.setup import import_from_ssh_config

    imported = 0
    errors: list[str] = []
    hosts_data = import_from_ssh_config(file_path)

    for item in hosts_data:
        try:
            port = validate_port(str(item.get("port", DEFAULT_SSH_PORT)))
            host = Host(
                name=item["name"],
                hostname=item.get("hostname", item["name"]),
                port=port,
                username=item.get("user"),
                private_key=item.get("identityfile"),
                jump_host=item.get("proxyjump"),
            )
            await ctx.hosts.create(host)
            imported += 1
        except Exception as e:
            errors.append(f"{item.get('name', '?')}: {e}")

    return imported, errors


async def _import_etc_hosts(ctx: SharedContext, content: str) -> tuple[int, list[str]]:
    """Import from /etc/hosts format."""
    imported = 0
    errors: list[str] = []

    for line_num, line in enumerate(content.splitlines(), 1):
        line = line.strip()

        if not line or line.startswith("#"):
            continue

        parts = line.split()
        if len(parts) < 2:
            continue

        ip_addr = parts[0]
        hostname = parts[1]

        if hostname in ("localhost", "localhost.localdomain", "broadcasthost"):
            continue
        if ip_addr in ("127.0.0.1", "::1", "255.255.255.255", "fe80::1%lo0"):
            continue

        try:
            name = hostname.replace(".", "-")
            existing = await ctx.hosts.get_by_name(name)
            if existing:
                continue

            host = Host(
                name=name,
                hostname=ip_addr,
                port=DEFAULT_SSH_PORT,
                tags=["etc-hosts"],
            )
            await ctx.hosts.create(host)
            imported += 1
        except Exception as e:
            errors.append(f"Line {line_num} ({hostname}): {e}")

    return imported, errors


def create_host_from_dict(item: dict[str, Any]) -> Host:
    """Create Host from dictionary with validated port and tags."""
    raw_tags = item.get("tags", [])
    valid_tags = [t for t in raw_tags if isinstance(t, str) and validate_tag(t)[0]]

    return Host(
        name=item["name"],
        hostname=item.get("hostname", item.get("host", item["name"])),
        port=validate_port(str(item.get("port", DEFAULT_SSH_PORT))),
        username=item.get("username", item.get("user")),
        tags=valid_tags,
        private_key=item.get("private_key", item.get("key")),
        jump_host=item.get("jump_host", item.get("bastion")),
    )


def host_to_dict(h: Host) -> dict[str, Any]:
    """Convert Host to dictionary for export."""
    item: dict[str, Any] = {"name": h.name, "hostname": h.hostname, "port": h.port}
    if h.username:
        item["username"] = h.username
    if h.tags:
        item["tags"] = h.tags
    if h.private_key:
        item["private_key"] = h.private_key
    if h.jump_host:
        item["jump_host"] = h.jump_host
    return item


def serialize_hosts(data: list[dict[str, Any]], file_format: str) -> str:
    """Serialize hosts data to string."""
    if file_format == "json":
        return json.dumps(data, indent=2)
    elif file_format == "yaml":
        import yaml

        return yaml.dump(data, default_flow_style=False)
    elif file_format == "csv":
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=["name", "hostname", "port", "username", "tags"])
        writer.writeheader()
        for item in data:
            row_item = dict(item)
            row_item["tags"] = ",".join(row_item.get("tags", []))
            writer.writerow(
                {k: row_item.get(k, "") for k in ["name", "hostname", "port", "username", "tags"]}
            )
        return output.getvalue()
    return json.dumps(data, indent=2)
