from __future__ import annotations

import contextlib
import os
import select
import shutil
import signal
import struct
import sys
from typing import Any

import typer
from rich.console import Console
from rich.panel import Panel


def _build_claude_command(resume: str | bool | None) -> list[str]:
    claude_path = shutil.which("claude")
    if not claude_path:
        raise RuntimeError("Claude Code CLI not found in PATH")

    cmd = [claude_path]
    if resume is True:
        cmd.append("--resume")
    elif isinstance(resume, str) and resume:
        cmd.extend(["--resume", resume])
    return cmd


def _set_pty_size(fd: int) -> None:
    import fcntl  # noqa: PLC0415
    import termios  # noqa: PLC0415

    size = os.get_terminal_size()
    winsize = struct.pack("HHHH", size.lines, size.columns, 0, 0)
    fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)


def _spawn_pty_child(
    *,
    master_fd: int,
    slave_fd: int,
    project_path: str,
    cmd: list[str],
) -> int:
    claude_path = cmd[0]
    pid = os.fork()
    if pid == 0:
        os.close(master_fd)
        os.setsid()
        os.dup2(slave_fd, 0)
        os.dup2(slave_fd, 1)
        os.dup2(slave_fd, 2)
        os.close(slave_fd)
        os.chdir(project_path)

        env = {
            **os.environ,
            "TERM": os.environ.get("TERM", "xterm-256color"),
            "COLORTERM": os.environ.get("COLORTERM", "truecolor"),
            "FORCE_COLOR": "1",
            "CLICOLOR": "1",
            "CLICOLOR_FORCE": "1",
        }
        os.execve(claude_path, cmd, env)
    return pid


def _install_resize_handler(master_fd: int) -> Any:
    import fcntl  # noqa: PLC0415
    import termios  # noqa: PLC0415

    def handle_sigwinch(_signum: int, _frame: object) -> None:
        try:
            size = os.get_terminal_size()
            winsize = struct.pack("HHHH", size.lines, size.columns, 0, 0)
            fcntl.ioctl(master_fd, termios.TIOCSWINSZ, winsize)
        except (OSError, ValueError):
            pass

    return signal.signal(signal.SIGWINCH, handle_sigwinch)


def _drain_remaining_output(master_fd: int) -> None:
    while True:
        r, _, _ = select.select([master_fd], [], [], 0.1)
        if not r:
            break
        try:
            data = os.read(master_fd, 4096)
            if data:
                os.write(sys.stdout.fileno(), data)
            else:
                break
        except OSError:
            break


def _run_local_io_loop(master_fd: int, pid: int) -> None:
    while True:
        r, _, _ = select.select([sys.stdin, master_fd], [], [], 0.1)
        if sys.stdin in r:
            data = os.read(sys.stdin.fileno(), 1024)
            if not data:
                break
            if b"\x11" in data:
                break
            os.write(master_fd, data)

        if master_fd in r:
            try:
                data = os.read(master_fd, 4096)
                if data:
                    os.write(sys.stdout.fileno(), data)
                else:
                    break
            except OSError:
                break

        result = os.waitpid(pid, os.WNOHANG)
        if result[0] != 0:
            _drain_remaining_output(master_fd)
            break


async def run_local_interactive_session(
    console: Console,
    *,
    resume: str | bool | None,
    project_path: str,
) -> None:
    import pty  # noqa: PLC0415
    import termios  # noqa: PLC0415
    import tty  # noqa: PLC0415

    console.print(
        Panel.fit(
            "[bold green]Starting Claude Code Session[/bold green]\n\n"
            f"[dim]Project:[/dim]  {project_path}\n"
            f"[dim]Resume:[/dim]   {resume or 'new session'}\n\n"
            "[yellow]Press Ctrl+Q to exit[/yellow]",
            title="Claude Board Chat",
            border_style="green",
        )
    )

    try:
        cmd = _build_claude_command(resume)
    except RuntimeError:
        console.print("[red]Claude Code CLI not found in PATH[/red]")
        raise typer.Exit(1) from None

    old_settings = termios.tcgetattr(sys.stdin)
    master_fd, slave_fd = pty.openpty()
    pid: int | None = None

    try:
        _set_pty_size(slave_fd)
        pid = _spawn_pty_child(
            master_fd=master_fd,
            slave_fd=slave_fd,
            project_path=project_path,
            cmd=cmd,
        )
        os.close(slave_fd)
        tty.setraw(sys.stdin)
        old_sigwinch = _install_resize_handler(master_fd)

        try:
            _run_local_io_loop(master_fd, pid)
        finally:
            signal.signal(signal.SIGWINCH, old_sigwinch)

    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
        os.close(master_fd)
        if pid is not None:
            with contextlib.suppress(ChildProcessError):
                os.waitpid(pid, 0)

    console.print("\n[dim]Session ended.[/dim]")

