import os
import posixpath
import shutil
from typing import List
from dataclasses import dataclass


@dataclass
class ContainerIntegrationMount:
    source_path: str
    container_path: str
    is_file: bool


CONTAINER_HOME_DIR = "/root"
CONTAINER_HOST_HOME_BASE = "/mim/home"
CONTAINER_SHELL_STATE_DIR = "/mim/shell-state"
CONTAINER_SHELL_HISTORY_DIR = CONTAINER_SHELL_STATE_DIR

CONTAINER_INTEGRATION_MOUNTS: List[ContainerIntegrationMount] = [
    ContainerIntegrationMount(
        "$/shell-state",
        CONTAINER_SHELL_STATE_DIR,
        False,
    ),
]


def get_container_integration_mounts(data_dir) -> List[ContainerIntegrationMount]:
    return [
        ContainerIntegrationMount(
            x.source_path.replace("$", data_dir),
            x.container_path.replace("$", CONTAINER_HOME_DIR),
            x.is_file,
        )
        for x in CONTAINER_INTEGRATION_MOUNTS
    ]


def destroy_container_data_dir(data_dir: str, keep_shell_state: bool):
    if not keep_shell_state:
        shutil.rmtree(data_dir)
        return

    if not os.path.isdir(data_dir):
        return

    preserved_paths = {
        os.path.realpath(os.path.abspath(mount.source_path))
        for mount in get_container_integration_mounts(data_dir)
        if mount.container_path == CONTAINER_SHELL_STATE_DIR
    }

    for entry in os.scandir(data_dir):
        entry_path = os.path.realpath(os.path.abspath(entry.path))
        if entry_path in preserved_paths:
            continue

        if entry.is_dir(follow_symlinks=False):
            shutil.rmtree(entry.path)
        else:
            os.unlink(entry.path)


def get_home_integration_mount() -> str:
    host_home = os.path.realpath(os.path.abspath(os.path.expanduser(get_home_dir())))
    container_home = get_container_host_home_dir()
    return f"{host_home}:{container_home}"


def get_home_integration_env() -> str:
    return f"HOST_HOME={get_container_host_home_dir()}"


def get_container_host_home_dir() -> str:
    host_home = os.path.realpath(os.path.abspath(os.path.expanduser(get_home_dir())))
    home_name = os.path.basename(host_home.rstrip(os.sep))
    return posixpath.join(CONTAINER_HOST_HOME_BASE, home_name)


def map_host_path_to_container(host_path: str, mounts) -> str | None:
    host_path = os.path.realpath(os.path.abspath(os.path.expanduser(host_path)))

    best_source = None
    best_destination = None
    for mount in mounts:
        source = mount.get("source")
        destination = mount.get("destination")
        if not source or not destination:
            continue

        source = os.path.realpath(os.path.abspath(source))
        try:
            common_path = os.path.commonpath([host_path, source])
        except ValueError:
            continue

        if common_path != source:
            continue

        if best_source is None or len(source) > len(best_source):
            best_source = source
            best_destination = destination

    if best_source is None or best_destination is None:
        return None

    rel_path = os.path.relpath(host_path, best_source)
    if rel_path == ".":
        return best_destination

    return posixpath.join(best_destination, rel_path.replace("\\", "/"))


def get_app_data_dir(app_name: str) -> str:
    import platform

    system = platform.system().lower()

    if system == "darwin":
        return os.path.join(
            os.environ["HOME"], f"Library/Application Support/{app_name}"
        )
    elif system == "linux":
        xdg_data = os.environ.get("XDG_DATA_HOME")
        if xdg_data:
            return os.path.join(xdg_data, app_name)
        else:
            return os.path.join(os.environ["HOME"], f".local/share/{app_name}")
    elif system == "windows":
        return os.path.join(os.environ["APPDATA"], app_name)
    else:
        raise NotImplementedError(f"unknown system: {system}")


def get_home_dir() -> str:
    if os.name == "posix":
        if os.uname().sysname == "Darwin":
            return os.environ["HOME"]
        elif os.uname().sysname == "Linux":
            return os.environ["HOME"]
        else:
            raise NotImplementedError(f"unknown posix system: {os.uname().sysname}")
    elif os.name == "nt":
        return os.environ["USERPROFILE"]
    else:
        raise NotImplementedError(f"unknown os: {os.name}")
