"""CLI for mkbooks — Create books from Markdown files."""

from __future__ import annotations

import http.server
import re
import socketserver
import time
import webbrowser
from pathlib import Path

import click
import yaml

from . import __version__
from .builder import build_book, init_skeleton
from .config import load_config

# Default config filenames (in order of preference)
DEFAULT_CONFIG_FILES = ["mkbooks.yml", "mkbooks.yaml", "book.yml", "book.yaml"]


def find_config_file(base_dir: Path) -> Path | None:
    """Find the config file in the given directory."""
    for filename in DEFAULT_CONFIG_FILES:
        config_path = base_dir / filename
        if config_path.exists():
            return config_path
    return None


def get_config_path(config_path: str | None, must_exist: bool = True) -> Path:
    """Resolve the config file path."""
    if config_path:
        path = Path(config_path).resolve()
        if must_exist and not path.exists():
            raise click.ClickException(f"Config file not found: {path}")
        return path

    # Auto-detect config file
    found = find_config_file(Path.cwd())
    if found:
        return found.resolve()

    if must_exist:
        raise click.ClickException(
            f"No config file found. Expected one of: {', '.join(DEFAULT_CONFIG_FILES)}"
        )
    return Path.cwd() / "mkbooks.yml"


@click.group()
@click.version_option(version=__version__, prog_name="mkbooks")
def main():
    """mkbooks — Create beautiful books from Markdown with PagedJS."""
    pass


@main.command()
@click.option(
    "-c",
    "--config",
    "config_path",
    default=None,
    type=click.Path(),
    help="Path to the YAML configuration file.",
)
@click.option(
    "--no-open",
    is_flag=True,
    default=False,
    help="Do not open the browser automatically.",
)
@click.option(
    "--pdf",
    is_flag=True,
    default=False,
    help="Generate PDF output (requires mkbooks[pdf]).",
)
def build(config_path: str | None, no_open: bool, pdf: bool):
    """Build the book from the YAML configuration."""
    config_file = get_config_path(config_path)
    base_dir = config_file.parent

    click.echo(f"📖 Loading configuration from {config_file.name}...")
    config = load_config(config_file)

    if not config.chapters:
        click.echo(f"⚠️  No chapters configured in {config_file.name}", err=True)
        raise SystemExit(1)

    click.echo(f"📚 Building: {config.title}")
    click.echo(f"   Author: {config.author}")
    click.echo(f"   Chapters: {len(config.chapters)}")

    try:
        output_file = build_book(config, base_dir, pdf=pdf)
        output_dir = output_file.parent

        if pdf:
            pdf_file = output_dir / f"{config.output.print_filename}.pdf"
            click.echo(f"\n✅ Reader generated: {output_file}")
            click.echo(f"   PDF version:     {pdf_file}")
        else:
            print_file = output_dir / f"{config.output.print_filename}.html"
            click.echo(f"\n✅ Reader generated: {output_file}")
            click.echo(f"   Print version:   {print_file}")

        if config.output.open_browser and not no_open:
            click.echo("🌐 Opening in browser...")
            webbrowser.open(f"file://{output_file}")

    except RuntimeError as e:
        click.echo(f"\n❌ {e}", err=True)
        raise SystemExit(1)
    except FileNotFoundError as e:
        click.echo(f"\n❌ Error: {e}", err=True)
        raise SystemExit(1)
    except Exception as e:
        click.echo(f"\n❌ Unexpected error: {e}", err=True)
        raise SystemExit(1)


@main.command()
@click.option(
    "-d",
    "--directory",
    default=".",
    type=click.Path(),
    help="Directory where the book skeleton will be created.",
)
@click.option(
    "-t",
    "--title",
    default="Mi Libro",
    help="Book title.",
)
@click.option(
    "-n",
    "--chapters",
    "num_chapters",
    default=1,
    type=int,
    help="Number of initial chapters.",
)
def init(directory: str, title: str, num_chapters: int):
    """Create a new book project in the current directory."""
    base_dir = Path(directory).resolve()

    click.echo(f"📁 Creating book skeleton in: {base_dir}")
    yml_path = init_skeleton(base_dir, title=title, num_chapters=num_chapters)

    click.echo("✅ Skeleton created successfully.")
    click.echo("\nFiles created:")
    click.echo(f"   📄 {yml_path.name} - Book configuration")

    for i in range(1, num_chapters + 1):
        click.echo(f"   📄 chapter-{i}.md   - Chapter {i}")

    click.echo(f"\n👉 Edit {yml_path.name} and run: mkbooks build")


@main.command()
@click.argument("project_name")
@click.option(
    "-t",
    "--title",
    default=None,
    help="Book title. Defaults to the project name.",
)
@click.option(
    "-n",
    "--chapters",
    "num_chapters",
    default=1,
    type=int,
    help="Number of initial chapters.",
)
def new(project_name: str, title: str | None, num_chapters: int):
    """Create a new book project in a new directory.

    PROJECT_NAME: Name of the directory to create for the book project.
    """
    base_dir = Path.cwd() / project_name

    if base_dir.exists():
        raise click.ClickException(f"Directory already exists: {base_dir}")

    book_title = title or project_name.replace("-", " ").replace("_", " ").title()

    click.echo(f"📁 Creating new book project: {project_name}")
    yml_path = init_skeleton(base_dir, title=book_title, num_chapters=num_chapters)

    click.echo("✅ Project created successfully.")
    click.echo("\nFiles created:")
    click.echo(f"   📄 {project_name}/{yml_path.name}")

    for i in range(1, num_chapters + 1):
        click.echo(f"   📄 {project_name}/chapter-{i}.md")

    click.echo(f"\n👉 cd {project_name} && mkbooks build")


def slugify(text: str) -> str:
    """Convert text to a URL-friendly slug."""
    text = text.lower()
    text = re.sub(r"[^\w\s-]", "", text)
    text = re.sub(r"[\s_]+", "-", text)
    return text.strip("-")


@main.command("add-chapter")
@click.argument("title")
@click.option(
    "-c",
    "--config",
    "config_path",
    default=None,
    type=click.Path(),
    help="Path to the YAML configuration file.",
)
@click.option(
    "-f",
    "--file",
    "filename",
    default=None,
    help="Custom filename for the chapter (e.g., 'my-chapter.md').",
)
def add_chapter(title: str, config_path: str | None, filename: str | None):
    """Add a new chapter to the book.

    TITLE: The title of the new chapter.
    """
    config_file = get_config_path(config_path)
    base_dir = config_file.parent

    # Load current config to determine next chapter number
    with open(config_file, encoding="utf-8") as f:
        raw_config = yaml.safe_load(f) or {}

    book_data = raw_config.get("book", raw_config)
    chapters = book_data.get("chapters", [])

    # Determine filename
    if filename:
        chapter_file = filename if filename.endswith(".md") else f"{filename}.md"
    else:
        # Auto-generate: chapter-N.md based on existing count
        existing_chapter_numbers = []
        for ch in chapters:
            match = re.match(r"chapter-(\d+)\.md", ch.get("file", ""), re.IGNORECASE)
            if match:
                existing_chapter_numbers.append(int(match.group(1)))

        next_num = max(existing_chapter_numbers, default=0) + 1
        chapter_file = f"chapter-{next_num}.md"

    chapter_path = base_dir / chapter_file

    # Check if file already exists
    if chapter_path.exists():
        raise click.ClickException(f"File already exists: {chapter_path}")

    # Create the chapter file
    chapter_path.write_text(
        f"# {title}\n\nEscribe aquí el contenido del capítulo.\n",
        encoding="utf-8",
    )

    # Add chapter to config
    new_chapter = {"file": chapter_file, "title": title}

    # Update YAML - ensure we have the 'book' structure
    if "book" in raw_config:
        if "chapters" not in raw_config["book"]:
            raw_config["book"]["chapters"] = []
        raw_config["book"]["chapters"].append(new_chapter)
    else:
        if "chapters" not in raw_config:
            raw_config["chapters"] = []
        raw_config["chapters"].append(new_chapter)

    with open(config_file, "w", encoding="utf-8") as f:
        yaml.dump(
            raw_config,
            f,
            default_flow_style=False,
            allow_unicode=True,
            sort_keys=False,
        )

    click.echo(f"✅ Chapter added: {chapter_file}")
    click.echo(f"   Title: {title}")
    click.echo(f"   Config updated: {config_file.name}")


@main.command()
@click.option(
    "-c",
    "--config",
    "config_path",
    default=None,
    type=click.Path(),
    help="Path to the YAML configuration file.",
)
@click.option(
    "-p",
    "--port",
    default=8765,
    type=int,
    help="Local server port.",
)
def serve(config_path: str | None, port: int):
    """Build the book and serve it with live-reload."""
    config_file = get_config_path(config_path)
    base_dir = config_file.parent
    config = load_config(config_file)

    click.echo("📖 Building book...")
    output_file = build_book(config, base_dir)
    output_dir = output_file.parent

    class QuietHandler(http.server.SimpleHTTPRequestHandler):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, directory=str(output_dir), **kwargs)

        def log_message(self, format, *args):
            pass  # Silence server logs

    try:
        with socketserver.TCPServer(("", port), QuietHandler) as httpd:
            url = f"http://localhost:{port}/{output_file.name}"
            click.echo(f"🌐 Server started at: {url}")
            click.echo("   Press Ctrl+C to stop.\n")

            # Open browser
            webbrowser.open(url)

            # Watch for changes
            try:
                from watchdog.observers import Observer
                from watchdog.events import FileSystemEventHandler

                class RebuildHandler(FileSystemEventHandler):
                    def __init__(self):
                        self._last_build = 0

                    def on_modified(self, event):
                        if event.is_directory:
                            return
                        src_path = event.src_path
                        if src_path.endswith((".md", ".yml", ".yaml")):
                            now = time.time()
                            if now - self._last_build < 1:
                                return
                            self._last_build = now
                            click.echo(
                                f"🔄 Change detected: {Path(src_path).name}, rebuilding..."
                            )
                            try:
                                new_config = load_config(config_file)
                                build_book(new_config, base_dir)
                                click.echo("✅ Book updated.")
                            except Exception as e:
                                click.echo(f"❌ Error: {e}")

                observer = Observer()
                observer.schedule(RebuildHandler(), str(base_dir), recursive=False)
                observer.start()
                click.echo("👁️  Watching for changes in .md and .yml files...")

            except ImportError:
                click.echo("⚠️  watchdog not installed. No live-reload.")

            httpd.serve_forever()

    except KeyboardInterrupt:
        click.echo("\n👋 Server stopped.")
    except OSError as e:
        click.echo(f"❌ Server error: {e}", err=True)


if __name__ == "__main__":
    main()
