"""
Build functions for EPUB generation.

Pipeline: mkdocs.yml → combine → normalize → pandoc → EPUB
"""

import subprocess
import sys
from pathlib import Path
from typing import Optional

import yaml


def _get_data_path(relative_path: str) -> Path:
    """
    Get path to data files (scripts, epub assets, etc.).

    Looks in installed location first (sys.prefix/share/text-forge/...),
    then falls back to repo-relative path for development.
    """
    # Try installed location (when installed via pip)
    installed = Path(sys.prefix) / "share" / "text-forge" / relative_path
    if installed.exists():
        return installed

    # Fall back to repo-relative path (for development)
    repo_relative = Path(__file__).parent.parent / relative_path
    if repo_relative.exists():
        return repo_relative

    raise FileNotFoundError(
        f"Could not find {relative_path} in installed location ({installed}) "
        f"or repo-relative location ({repo_relative})"
    )


def load_mkdocs_config(config_path: Path) -> dict:
    """Load mkdocs.yml configuration."""
    if not config_path.exists():
        raise FileNotFoundError(f"Config file not found: {config_path}")

    with open(config_path, "r", encoding="utf-8") as f:
        # Handle MkDocs custom tags like !ENV
        class IgnoreUnknownLoader(yaml.SafeLoader):
            pass

        def unknown_constructor(loader, tag_suffix, node):
            if isinstance(node, yaml.ScalarNode):
                return loader.construct_scalar(node)
            elif isinstance(node, yaml.SequenceNode):
                return loader.construct_sequence(node)
            elif isinstance(node, yaml.MappingNode):
                return loader.construct_mapping(node)
            return None

        IgnoreUnknownLoader.add_multi_constructor("!", unknown_constructor)
        config = yaml.load(f, Loader=IgnoreUnknownLoader)

    return config


def combine_chapters(config_path: Path, output_path: Path) -> None:
    """
    Combine markdown chapters from mkdocs.yml navigation.

    Uses mkdocs-combine.py script.
    """
    script = _get_data_path("scripts/mkdocs-combine.py")

    print(f"Combining chapters from {config_path}...")

    with open(output_path, "w", encoding="utf-8") as out:
        subprocess.run(
            [sys.executable, str(script), str(config_path)],
            stdout=out,
            stderr=subprocess.PIPE,
            text=True,
            check=True,
        )

    print(f"✓ Combined markdown saved to {output_path}")


def normalize_markdown(input_path: Path, output_path: Path) -> None:
    """
    Normalize PyMdown syntax to Pandoc-compatible markdown.

    Uses pymdown-pandoc.lua filter.
    """
    lua_filter = _get_data_path("scripts/pymdown-pandoc.lua")

    print("Normalizing markdown syntax...")

    subprocess.run(
        [
            "pandoc",
            str(input_path),
            "--from",
            "markdown",
            "--to",
            "markdown",
            "--lua-filter",
            str(lua_filter),
            "--output",
            str(output_path),
        ],
        stderr=subprocess.PIPE,
        text=True,
        check=True,
    )

    print(f"✓ Normalized markdown saved to {output_path}")


def process_epub_metadata(config_path: Path, output_path: Path) -> None:
    """
    Generate EPUB metadata YAML from mkdocs.yml.

    Uses process-epub-meta.py script.
    """
    import datetime
    import subprocess

    script = _get_data_path("scripts/process-epub-meta.py")
    meta_template = _get_data_path("epub/book_meta.yml")

    print("Processing EPUB metadata...")

    # Get git info for edition and date (matching Makefile implementation)
    # Try to get most recent tag first
    git_tag = None
    try:
        git_result = subprocess.run(
            ["git", "describe", "--tags", "--abbrev=0"],
            capture_output=True,
            text=True,
            check=True,
            cwd=config_path.parent,
        )
        git_tag = git_result.stdout.strip()
    except subprocess.CalledProcessError:
        pass
    
    # Fallback to describe if no tag found
    if not git_tag:
        try:
            git_result = subprocess.run(
                ["git", "describe", "--tags", "--always"],
                capture_output=True,
                text=True,
                check=True,
                cwd=config_path.parent,
            )
            git_tag = git_result.stdout.strip()
        except subprocess.CalledProcessError:
            git_tag = "dev"

    # Get date from last commit or use today
    date = datetime.datetime.now().strftime("%Y-%m-%d")
    try:
        git_result = subprocess.run(
            ["git", "log", "-1", "--format=%cs"],
            capture_output=True,
            text=True,
            check=True,
            cwd=config_path.parent,
        )
        if git_result.stdout.strip():
            date = git_result.stdout.strip()
    except subprocess.CalledProcessError:
        pass

    # Format date with Russian month names (matching Makefile)
    date_obj = datetime.datetime.strptime(date, "%Y-%m-%d")
    russian_months = {
        "January": "января", "February": "февраля", "March": "марта",
        "April": "апреля", "May": "мая", "June": "июня",
        "July": "июля", "August": "августа", "September": "сентября",
        "October": "октября", "November": "ноября", "December": "декабря"
    }
    date_display = date_obj.strftime("%d %B %Y")
    for eng, rus in russian_months.items():
        date_display = date_display.replace(eng, rus)
    
    # Combine tag and formatted date as edition (like Makefile: "v0.51.0b1, 29 января 2026")
    edition = f"{git_tag}, {date_display}"

    subprocess.run(
        [
            sys.executable,
            str(script),
            "--mkdocs-config",
            str(config_path),
            "--template",
            str(meta_template),
            "--out",
            str(output_path),
            "--edition",
            edition,
            "--date",
            date,
        ],
        check=True,
        capture_output=True,
        text=True,
    )

    print(f"✓ EPUB metadata saved to {output_path}")


def build_epub_with_pandoc(
    markdown_path: Path,
    metadata_path: Path,
    css_path: Path,
    cover_path: Optional[Path],
    output_path: Path,
    docs_dir: Path,
) -> None:
    """
    Build EPUB using pandoc.
    """
    print("Building EPUB with pandoc...")

    cmd = [
        "pandoc",
        str(markdown_path),
        "--from",
        "markdown+smart",
        "--to",
        "epub3",
        "--standalone",
        "--toc",
        "--toc-depth=2",
        "--metadata-file",
        str(metadata_path),
        "--resource-path",
        str(docs_dir),
        "--css",
        str(css_path),
        "--output",
        str(output_path),
    ]

    if cover_path and cover_path.exists():
        cmd.extend(["--epub-cover-image", str(cover_path)])

    subprocess.run(cmd, check=True, capture_output=True, text=True)

    print(f"✓ EPUB saved to {output_path}")


def build_epub_pipeline(
    config_path: Path,
    build_dir: Path,
    epub_filename: str = "text_book.epub",
    combined_filename: str = "text_combined.md",
) -> Path:
    """
    Run complete EPUB build pipeline.

    Returns path to generated EPUB file.
    """
    config_path = config_path.resolve()
    content_root = config_path.parent
    build_dir.mkdir(parents=True, exist_ok=True)

    # Load config to get docs_dir and other settings
    config = load_mkdocs_config(config_path)
    docs_dir = Path(config.get("docs_dir", "docs"))

    # Pipeline paths
    combined_md = build_dir / combined_filename
    normalized_md = build_dir / "pandoc.md"
    metadata_yaml = build_dir / "book_meta.yml"
    epub_file = build_dir / epub_filename

    # Static assets
    css_file = _get_data_path("epub/epub.css")
    cover_file = content_root / docs_dir / "img" / "cover.jpg"

    try:
        # Step 1: Combine chapters
        combine_chapters(config_path, combined_md)

        # Step 2: Normalize markdown
        normalize_markdown(combined_md, normalized_md)

        # Step 3: Process metadata
        process_epub_metadata(config_path, metadata_yaml)

        # Step 4: Build EPUB
        build_epub_with_pandoc(
            normalized_md,
            metadata_yaml,
            css_file,
            cover_file if cover_file.exists() else None,
            epub_file,
            docs_dir,
        )

        print(f"\n✓ EPUB build complete: {epub_file}")
        return epub_file

    except subprocess.CalledProcessError as e:
        print(f"✗ Build failed: {e}", file=sys.stderr)
        if e.stderr:
            print(e.stderr, file=sys.stderr)
        raise
    except FileNotFoundError as e:
        print(f"✗ Build failed: {e}", file=sys.stderr)
        raise


def build_mkdocs_site(
    config_path: Path, site_dir: Optional[Path] = None, strict: bool = False
) -> Path:
    """
    Build MkDocs site.

    Returns path to site directory.
    """
    config_path = config_path.resolve()

    # Load config to get site_dir
    config = load_mkdocs_config(config_path)
    if site_dir is None:
        site_dir = Path(config.get("site_dir", "public"))

    print("\nBuilding MkDocs site...")

    cmd = [sys.executable, "-m", "mkdocs", "build", "--config-file", str(config_path)]
    if site_dir:
        cmd.extend(["--site-dir", str(site_dir)])
    if strict:
        cmd.append("--strict")

    try:
        subprocess.run(
            cmd,
            check=True,
        )
    except subprocess.CalledProcessError as e:
        raise

    print(f"✓ Site built to {site_dir}")
    return site_dir


def copy_build_artifacts(
    build_dir: Path,
    site_dir: Path,
    epub_filename: str = "text_book.epub",
    combined_filename: str = "text_combined.md",
) -> None:
    """
    Copy build artifacts (EPUB, combined text) to site root directory.

    This makes them available for download from the published site.
    Files are placed at site root so combined markdown can reference
    relative paths like img/... which work from that location.
    """
    import shutil

    site_dir.mkdir(parents=True, exist_ok=True)

    print(f"\nCopying build artifacts to {site_dir}...")

    # Copy EPUB
    epub_src = build_dir / epub_filename
    if epub_src.exists():
        shutil.copy2(epub_src, site_dir / epub_filename)
        print(f"✓ Copied {epub_src.name}")

    # Copy combined text
    combined_src = build_dir / combined_filename
    if combined_src.exists():
        shutil.copy2(combined_src, site_dir / combined_filename)
        print(f"✓ Copied {combined_src.name}")


def create_legacy_redirect_404(site_dir: Path) -> None:
    """
    Create 404.html that redirects legacy /ru/* URLs to new structure.

    For GitHub Pages, 404.html is served for all missing pages.
    This detects /ru/* URLs and redirects to non-prefixed versions.
    
    Args:
        site_dir: Site directory where 404.html will be created
    """
    site_dir.mkdir(parents=True, exist_ok=True)
    
    redirect_file = site_dir / "404.html"
    
    html = """<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Redirecting...</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100vh;
      margin: 0;
      background: #f5f5f5;
    }
    .message {
      text-align: center;
      padding: 2rem;
    }
  </style>
  <script>
    (function() {
      const path = window.location.pathname;
      
      // Detect legacy /ru/ prefixed URL and redirect
      if (path.startsWith('/ru/')) {
        const newPath = path.replace('/ru/', '/');
        const newUrl = window.location.origin + newPath + window.location.search + window.location.hash;
        window.location.replace(newUrl);
      } else {
        // Real 404 - redirect to home after delay
        setTimeout(function() {
          window.location.replace('/');
        }, 2000);
      }
    })();
  </script>
</head>
<body>
  <div class="message">
    <h1>Redirecting...</h1>
    <p>If you are not redirected automatically, <a href="/">click here</a>.</p>
  </div>
</body>
</html>"""
    
    redirect_file.write_text(html, encoding="utf-8")
    print(f"✓ Created 404 redirect: {redirect_file} (redirects /ru/* → /*)")


def build_site_pipeline(
    config_path: Path,
    build_dir: Path,
    site_dir: Optional[Path] = None,
    strict: bool = False,
    copy_artifacts: bool = True,
    create_404_redirect: bool = True,
) -> None:
    """
    Run complete site + EPUB build pipeline.

    1. Build EPUB pipeline (generates combined markdown + EPUB)
    2. Build MkDocs site
    3. Copy artifacts to site_dir root (optional, after site build to avoid link warnings)
    4. Create legacy URL redirect via 404.html (optional, redirects /ru/* to /*)
    """
    config_path = config_path.resolve()
    content_root = config_path.parent

    # Load config
    config = load_mkdocs_config(config_path)
    docs_dir = content_root / Path(config.get("docs_dir", "docs"))

    # Extract text-forge plugin config for filenames
    plugin_config = {}
    for plugin in config.get("plugins", []):
        if isinstance(plugin, dict) and "text-forge" in plugin:
            plugin_config = plugin["text-forge"]
            break
    
    epub_filename = plugin_config.get("epub_filename", "text_book.epub")
    combined_filename = plugin_config.get("combined_filename", "text_combined.md")

    try:
        # Step 1: Build EPUB
        epub_file = build_epub_pipeline(
            config_path, build_dir, epub_filename, combined_filename
        )

        # Step 2: Build site
        built_site_dir = build_mkdocs_site(config_path, site_dir, strict)

        # Step 3: Copy artifacts after site build (optional)
        if copy_artifacts:
            copy_build_artifacts(build_dir, built_site_dir, epub_filename, combined_filename)

        # Step 4: Create 404 redirect for legacy URLs (optional)
        if create_404_redirect:
            create_legacy_redirect_404(built_site_dir)

        print("\n✓ Full build complete!")
        print(f"  EPUB: {epub_file}")
        print(f"  Site: {built_site_dir}")

    except Exception as e:
        print(f"\n✗ Build failed: {e}", file=sys.stderr)
        raise
