from __future__ import annotations

import contextlib
import os
import shutil
import socket
import subprocess
import sys
from pathlib import Path

import qrcode
from rich.console import Console

# Default paths
DEFAULT_SOCKET_PATH = Path.home() / ".claude-board" / "claude-board.sock"
DEFAULT_PID_FILE = Path.home() / ".claude-board" / "claude-board.pid"
SESSIONS_DIR = Path.home() / ".claude-board" / "sessions"

# Magic number constants
MIN_PARTS_COUNT = 2
HTTP_NOT_FOUND = 404
HTTP_BAD_REQUEST = 400
EXIT_CODE_SESSION_ENDED = 2


def get_primary_interface() -> str | None:
    """Get primary network interface name from default route."""
    try:
        if sys.platform == "darwin":
            result = subprocess.run(
                ["route", "-n", "get", "default"],
                capture_output=True,
                text=True,
                timeout=5,
                check=False,
            )
            for raw_line in result.stdout.split("\n"):
                stripped = raw_line.strip()
                if stripped.startswith("interface:"):
                    return stripped.split(":", 1)[1].strip()
        else:
            result = subprocess.run(
                ["ip", "route", "show", "default"],
                capture_output=True,
                text=True,
                timeout=5,
                check=False,
            )
            parts = result.stdout.strip().split()
            if "dev" in parts:
                dev_idx = parts.index("dev")
                if dev_idx + 1 < len(parts):
                    return parts[dev_idx + 1]
    except (OSError, subprocess.SubprocessError):
        pass
    return None


def get_network_interfaces() -> list[tuple[str, str, bool]]:
    """Get all interfaces as (name, ip, is_primary)."""
    interfaces: list[tuple[str, str, bool]] = []
    primary_iface = get_primary_interface()

    try:
        if sys.platform == "darwin":
            result = subprocess.run(
                ["ifconfig"],
                capture_output=True,
                text=True,
                timeout=5,
                check=False,
            )
            current_iface = None
            for line in result.stdout.split("\n"):
                if line and not line.startswith("\t") and not line.startswith(" "):
                    current_iface = line.split(":")[0]
                elif "inet " in line and current_iface:
                    parts = line.strip().split()
                    if len(parts) >= MIN_PARTS_COUNT:
                        ip = parts[1]
                        if not ip.startswith("127."):
                            interfaces.append(
                                (current_iface, ip, current_iface == primary_iface)
                            )
        else:
            result = subprocess.run(
                ["ip", "addr"],
                capture_output=True,
                text=True,
                timeout=5,
                check=False,
            )
            current_iface = None
            for line in result.stdout.split("\n"):
                if ": " in line and not line.startswith(" "):
                    parts = line.split(": ")
                    if len(parts) >= MIN_PARTS_COUNT:
                        current_iface = parts[1].split("@")[0]
                elif "inet " in line and current_iface:
                    parts = line.strip().split()
                    if len(parts) >= MIN_PARTS_COUNT:
                        ip = parts[1].split("/")[0]
                        if not ip.startswith("127."):
                            interfaces.append(
                                (current_iface, ip, current_iface == primary_iface)
                            )
    except (OSError, subprocess.SubprocessError):
        pass

    if interfaces and not any(is_primary for _, _, is_primary in interfaces):
        with contextlib.suppress(OSError):
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.connect(("8.8.8.8", 80))
            primary_ip = sock.getsockname()[0]
            sock.close()
            interfaces = [(iface, ip, ip == primary_ip) for iface, ip, _ in interfaces]

    interfaces.sort(key=lambda x: (not x[2], x[0]))
    return interfaces


def get_local_ip() -> str:
    """Get primary local IP address."""
    interfaces = get_network_interfaces()
    for _iface, ip, is_primary in interfaces:
        if is_primary:
            return ip
    if interfaces:
        return interfaces[0][1]
    return "unknown"


def write_pid_file(pid: int) -> None:
    """Write daemon PID file."""
    DEFAULT_PID_FILE.parent.mkdir(parents=True, exist_ok=True)
    DEFAULT_PID_FILE.write_text(str(pid))


def read_pid_file() -> int | None:
    """Read daemon PID file if valid/running."""
    if not DEFAULT_PID_FILE.exists():
        return None
    try:
        pid = int(DEFAULT_PID_FILE.read_text().strip())
        os.kill(pid, 0)
    except (ValueError, ProcessLookupError, PermissionError):
        DEFAULT_PID_FILE.unlink(missing_ok=True)
        return None
    else:
        return pid


def remove_pid_file() -> None:
    """Remove daemon PID file."""
    DEFAULT_PID_FILE.unlink(missing_ok=True)


def format_network_urls(port: int) -> str:
    """Format URLs for display with primary interface first."""
    interfaces = get_network_interfaces()
    lines = [f"[dim]Local:[/dim]      http://localhost:{port}"]
    for iface, ip, is_primary in interfaces:
        if is_primary:
            lines.append(
                f"[bold]Network:[/bold]    http://{ip}:{port}"
                f"  [green]← primary ({iface})[/green]"
            )
        else:
            lines.append(f"[dim]Network:[/dim]    http://{ip}:{port}  [dim]({iface})[/dim]")
    return "\n".join(lines)


def print_qr_code(console: Console, url: str) -> None:
    """Render a terminal-friendly QR code for URL."""
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,  # type: ignore[attr-defined]
        box_size=1,
        border=2,
    )
    qr.add_data(url)
    qr.make(fit=True)

    matrix = qr.get_matrix()
    rows = len(matrix)
    output_lines: list[str] = []

    for y in range(0, rows, 2):
        line = ""
        for x in range(len(matrix[0])):
            top = matrix[y][x] if y < rows else False
            bottom = matrix[y + 1][x] if y + 1 < rows else False

            if not top and not bottom:
                line += "█"
            elif not top:
                line += "▀"
            elif not bottom:
                line += "▄"
            else:
                line += " "
        output_lines.append(line)

    console.print("\n[dim]Scan to open on mobile:[/dim]")
    console.print(f"  [dim]{url}[/dim]")
    console.print()
    for line in output_lines:
        console.print(f"  {line}")
    console.print()

