from __future__ import annotations

import os
import shlex
import socket
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Iterable


def borg_env(passphrase: str | None = None) -> dict[str, str]:
    env = os.environ.copy()

    if passphrase:
        env["BORG_PASSPHRASE"] = passphrase

    return env


def build_archive_name(prefix: str | None = None) -> str:
    """
    Build a borg archive name.

    Default format:
        hostname-YYYY-MM-DD_HH-MM-SS

    With prefix:
        prefix-hostname-YYYY-MM-DD_HH-MM-SS
    """
    hostname = socket.gethostname()
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

    if prefix:
        return f"{prefix}-{hostname}-{timestamp}"

    return f"{hostname}-{timestamp}"


def normalize_include_paths(include_paths: Iterable[str | Path]) -> list[str]:
    normalized: list[str] = []
    seen: set[str] = set()

    for path in include_paths:
        resolved = str(Path(path))

        if resolved not in seen:
            seen.add(resolved)
            normalized.append(resolved)

    return normalized


def build_borg_create_command(
    repo: str,
    include_paths: Iterable[str | Path],
    archive_name: str | None = None,
    stats: bool = True,
    progress: bool = True,
) -> list[str]:
    normalized_paths = normalize_include_paths(include_paths)

    if not normalized_paths:
        raise ValueError("No include paths provided for borg backup.")

    if archive_name is None:
        archive_name = build_archive_name()

    command = ["borg", "create"]

    if stats:
        command.append("--stats")

    if progress:
        command.append("--progress")

    command.append(f"{repo}::{archive_name}")
    command.extend(normalized_paths)

    return command


def command_to_shell(command: list[str]) -> str:
    return " ".join(shlex.quote(part) for part in command)


def run_borg_create(
    repo: str,
    include_paths: Iterable[str | Path],
    passphrase: str | None = None,
    archive_name: str | None = None,
    stats: bool = True,
    progress: bool = True,
    quiet: bool = False,
) -> int:
    command = build_borg_create_command(
        repo=repo,
        include_paths=include_paths,
        archive_name=archive_name,
        stats=stats,
        progress=progress,
    )

    stdout = subprocess.DEVNULL if quiet else None
    stderr = subprocess.DEVNULL if quiet else None

    result = subprocess.run(
        command,
        env=borg_env(passphrase),
        stdout=stdout,
        stderr=stderr,
        check=False,
    )

    return result.returncode
