from __future__ import annotations

import asyncio
import sys
from pathlib import Path
from typing import Tuple

import click
from loguru import logger
from termcolor import colored

from .options import DocToolsOptions
from .workspace import BuildWorkspace

# FIXME: we still need pandoc to be present on the host system


async def build_main(*args: str) -> int | None:
    workspace = BuildWorkspace(args, DocToolsOptions())

    try:
        await workspace.prerequisites()
    except click.UsageError:
        raise
    except Exception as e:
        raise click.UsageError(str(e), ctx=click.get_current_context()) from e

    async with workspace.start():
        logger.info("Building docs with Sphinx")

        source = workspace.source_root.resolve().relative_to(Path.cwd())
        logger.info(f"Reading docs from {source}")

        build_args = workspace.sphinx_build_args
        logger.info(f"Running Sphinx with args: {' '.join(build_args)}")

        proc = await asyncio.create_subprocess_exec(
            sys.executable,
            "-m",
            "sphinx",
            *build_args,
            stdout=None,
            stderr=None,
        )

        if await proc.wait() != 0:
            logger.error("Sphinx exited with non-zero status code. Abort.")
            return proc.returncode

        logger.info(f"Building static site using image {workspace.image_name}")

        proc = await asyncio.create_subprocess_exec(
            *workspace.create_docker_command(
                "static-site",
                "build",
                workspace.remote_markdown_dir,
                workspace.remote_static_site_dir,
            ),
            stdout=None,
            stderr=None,
        )

        if await proc.wait() != 0:
            logger.error("Static site builder exited with non-zero status code.")
            return proc.returncode

        markdown_output = workspace.local_markdown_dir.relative_to(Path.cwd())
        static_site_output = workspace.local_static_site_dir.relative_to(Path.cwd())

        logger.success(f"MDX files stored at {markdown_output}")
        logger.success(f"HTML artifacts emitted to {static_site_output}")

        serve_command = colored(
            f"secretflow-doctools serve http {static_site_output}",
            "cyan",
        )
        logger.success(f"Preview by running {serve_command}")

        return 0


@click.command(
    context_settings={
        "ignore_unknown_options": True,
        "allow_extra_args": True,
    },
    short_help="Compile a Sphinx project into a static HTML website",
)
@click.argument("sphinx-args", nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def build(ctx: click.Context, sphinx_args: Tuple[str, ...]):
    """
    Compile a Sphinx project into an HTML website that can be opened in the browser.

    Docker must be available.

    ## Usage

    This command accepts all arguments that `sphinx-build` accepts, except for `-b`
    (builder) which is fixed to `mdx`.

    For example, for a `sphinx-build` invocation:

    \b
    ```bash
    sphinx-build -b html -T -E -D language=fr source build
    ```

    You would write:

    \b
    ```
    secretflow-doctools build -T -E -D language=fr source build
    ```

    \b
    ## The building process

    Building consists of 2 steps:

    1. Invoke Sphinx with the supplied options to generate [MDX](https://mdxjs.com/)
    (Markdown React) files, and collect all auxiliary assets into `<outputdir>/mdx`.
    2. Use a Node-based static site generator (SSG) to compile the MDX files into a
    bundle of static HTML, CSS, and JS files. This is much like Sphinx's own HTML
    builder.

    Currently the SSG runs in a Docker container (from a prebuilt image), but this may
    change in the future as we explore more options. doctools will try to pull the image
    if it's not already available locally.

    \b
    ## Output

    Building generates two output directories. Assuming you specified `./build` as the
    `outputdir` argument to Sphinx:

    - `./build/html` will contain the eventual HTML/CSS/JS artifacts. You can preview it
    with any static file server, e.g. `python -m http.server` or `npx serve`.
    `secretflow-doctools` comes with one: `secretflow-doctools serve http ./build/html`
    - `./build/mdx` will contain the MDX files that were generated by Sphinx. You can
    keep them around as they contain serialized environment data that could speed up
    future builds.
    """
    exit(asyncio.run(build_main(*ctx.args, *sphinx_args)))
