#!/usr/bin/env python3
# SPDX-License-Identifier: MIT

"""
A Python script to generate or update alias pages.

Disclaimer: This script generates a lot of false positives so it isn't suggested to use the sync option. If used, only stage changes and commit verified changes for your language by using -l LANGUAGE.

Note: If the current directory or one of its parents is called "tldr", the script will assume it is the tldr root, i.e., the directory that contains a clone of https://github.com/tldr-pages/tldr
If you aren't, the script will use TLDR_ROOT as the tldr root. Also, ensure 'git' is available.

Note: This script uses an interactive prompt instead of positional arguments to:
- Prevent argument parsing errors with command names containing dashes (e.g. 'pacman -S')
- Provide clearer guidance for required inputs
- Allow for input validation before page creation

Usage:
    python3 scripts/set-alias-page.py [-p PAGE] [-S] [-l LANGUAGE] [-s] [-n]

Options:
    -p, --page PAGE
        Specify the alias page in the format "platform/alias_command.md".
        This will start an interactive prompt to create/update the page.
    -S, --sync
        Synchronize each translation's alias page (if exists) with that of the English page.
    -l, --language LANGUAGE
        Specify the language, a POSIX Locale Name in the form of "ll" or "ll_CC" (e.g. "fr" or "pt_BR").
    -s, --stage
        Stage modified pages (requires 'git' on $PATH and TLDR_ROOT to be a Git repository).
    -n, --dry-run
        Show what changes would be made without actually modifying the page.

Examples:
    1. Create a new alias page interactively:
       python3 scripts/set-alias-page.py -p osx/gsum
       python3 scripts/set-alias-page.py --page osx/gsum
       This will start a wizard that guides you through creating the page.

    2. Read English alias pages and synchronize them into all translations:
       python3 scripts/set-alias-page.py -S
       python3 scripts/set-alias-page.py --sync

    3. Read English alias pages and synchronize them for Brazilian Portuguese pages only:
       python3 scripts/set-alias-page.py -S -l pt_BR
       python3 scripts/set-alias-page.py --sync --language pt_BR

    4. Read English alias pages, synchronize them into all translations and stage modified pages for commit:
       python3 scripts/set-alias-page.py -Ss
       python3 scripts/set-alias-page.py --sync --stage

    5. Read English alias pages and show what changes would be made:
       python3 scripts/set-alias-page.py -Sn
       python3 scripts/set-alias-page.py --sync --dry-run
"""

import re
from pathlib import Path
from dataclasses import dataclass
from _common import (
    IGNORE_FILES,
    Colors,
    get_tldr_root,
    get_pages_dir,
    get_target_paths,
    get_locale,
    get_status,
    stage,
    create_colored_line,
    create_argument_parser,
)


@dataclass
class Config:
    """Global configuration for the script"""

    root: Path
    pages_dirs: list[Path]
    templates: dict[str, str]
    dry_run: bool = False
    language: str = ""


@dataclass
class AliasPageContent:
    """Content of an alias page"""

    title: str
    original_command: str
    documentation_command: str


@dataclass
class AliasPage:
    """Represents an alias page with its path and content"""

    page_path: str
    content: AliasPageContent


IGNORE_FILES += ("tldr.md", "aria2.md")


def test_ignore_files():
    assert IGNORE_FILES == (
        ".DS_Store",
        "tldr.md",
        "aria2.md",
    )
    assert ".DS_Store" in IGNORE_FILES
    assert "tldr.md" in IGNORE_FILES


def get_templates(root: Path):
    """
    Get all alias page translation templates from
    TLDR_ROOT/contributing-guides/translation-templates/alias-pages.md.

    Parameters:
        root (Path): The path of local tldr repository, i.e., TLDR_ROOT.

    Returns:
        dict of (str, str): Language labels map to alias page templates.
    """

    template_file = root / "contributing-guides/translation-templates/alias-pages.md"
    with template_file.open(encoding="utf-8") as f:
        lines = f.readlines()

    # Parse alias-pages.md
    templates = {}
    i = 0
    while i < len(lines):
        if lines[i].startswith("###"):
            lang = lines[i][4:].strip("\n").strip(" ")
            while True:
                i = i + 1
                if lines[i].startswith("Not translated yet."):
                    is_translated = False
                    break
                elif lines[i].startswith("```markdown"):
                    i = i + 1
                    is_translated = True
                    break

            if is_translated:
                text = ""
                while not lines[i].startswith("```"):
                    text += lines[i]
                    i = i + 1
                templates[lang] = text

        i = i + 1

    return templates


def generate_alias_page_content(
    template_content: str,
    page_content: AliasPageContent,
) -> str:
    """
    Generate alias page content by replacing placeholders in the template.

    Parameters:
        template_content (str): The markdown template for the specific language.
        page_content (AliasPageContent): The content of the alias page

    Returns:
        str: The complete markdown content for the alias page.
    """

    template_command = "example"

    # Replace placeholders in template with actual values
    result = template_content.replace(template_command, page_content.title, 1)
    result = result.replace(template_command, page_content.original_command, 1)
    result = result.replace(template_command, page_content.documentation_command)

    return result


def set_alias_page(
    path: Path,
    page_content: AliasPageContent,
) -> str:
    """
    Write an alias page to disk.

    Parameters:
        path (Path): Path to an alias page
        page_content (AliasPageContent): The content to write to the page

    Returns:
        str: Execution status
             "" if the alias page standing for the same command already exists or if the locale does not match language_to_update.
             "\x1b[36mpage added"
             "\x1b[34mpage updated"
             "\x1b[36mpage would be added"
             "\x1b[34mpage would updated"
    """

    locale = get_locale(path)
    if locale not in config.templates or (
        config.language != "" and locale != config.language
    ):
        return ""

    # Get existing alias command from the locale page
    existing_locale_page_content = get_alias_command_in_page(
        path, get_locale_alias_pattern(locale)
    )

    if (
        existing_locale_page_content.documentation_command
        == page_content.documentation_command
    ):
        return ""

    new_locale_page_content = generate_alias_page_content(
        config.templates[locale],
        page_content,
    )

    # Determine status and write file
    status = get_status(
        "added" if not path.exists() else "updated",
        config.dry_run,
        "page",
    )

    if not config.dry_run:
        path.parent.mkdir(parents=True, exist_ok=True)
        with path.open("w", encoding="utf-8") as f:
            f.write(new_locale_page_content)

    return status


def get_locale_alias_pattern(locale: str) -> str:
    """Get alias pattern from template"""

    template_line = re.search(r">.*`example`", config.templates[locale]).group(0)
    locale_alias_pattern = template_line[2 : template_line.find("`example`")].strip()
    return locale_alias_pattern


def get_alias_command_in_page(path: Path, alias_pattern: str) -> AliasPageContent:
    """
    Determine whether the given path is an alias page.

    Returns:
        AliasPageContent: The page content, or empty strings if not an alias page
    """

    if not path.exists():
        return AliasPageContent(title="", original_command="", documentation_command="")

    with path.open(encoding="utf-8") as f:
        content = f.read()

    lines = content.splitlines()

    title = next((line.strip("# \n") for line in lines if line.startswith("# ")), "")

    command_lines = [line for line in lines if "`" in line]

    if len(command_lines) != 2 or not title:
        return AliasPageContent(title="", original_command="", documentation_command="")

    original_command = ""
    documentation_command = ""

    alias_line = next((line for line in command_lines if alias_pattern in line), None)
    if alias_line:
        description_match = re.search(r"`([^`]+)`", alias_line)
        if description_match:
            original_command = description_match[1]

    tldr_line = next(
        (line for line in command_lines if line.strip().startswith("`tldr")), None
    )
    if tldr_line:
        tldr_match = re.search(r"`tldr (.+)`", tldr_line.strip())
        if tldr_match:
            documentation_command = tldr_match[1]

    return AliasPageContent(
        title=title,
        original_command=original_command,
        documentation_command=documentation_command,
    )


def sync_alias_page_to_locale(pages_dir: Path, alias_page: AliasPage) -> list[Path]:
    """
    Synchronize an alias page into a specific locale directory.

    Parameters:
        pages_dir (Path): Directory containing pages for a specific locale
        alias_page (AliasPage): The alias page to sync

    Returns:
        list[Path]: List of paths that were modified
    """

    paths = []
    path = config.root / pages_dir / alias_page.page_path
    status = set_alias_page(path, alias_page.content)
    if status != "":
        rel_path = "/".join(path.parts[-3:])
        paths.append(rel_path)
        print(create_colored_line(Colors.GREEN, f"{rel_path} {status}"))
    return paths


def get_english_alias_pages(en_path: Path) -> list[AliasPage]:
    """
    Get all English alias pages with their commands.

    Parameters:
        en_path (Path): Path to English pages directory

    Returns:
        list[AliasPage]: List of alias pages with their content
    """

    alias_pages = []
    alias_pattern = get_locale_alias_pattern("en")

    # Get all platform directories (common, linux, etc.)
    platforms = [
        page.name for page in en_path.iterdir() if page.name not in IGNORE_FILES
    ]

    # Iterate through each platform
    for platform in platforms:
        platform_path = en_path / platform
        page_paths = [
            f"{platform}/{page.name}"
            for page in platform_path.iterdir()
            if page.name not in IGNORE_FILES
        ]

        # Check each command if it's an alias
        for page_path in page_paths:
            page_content = get_alias_command_in_page(en_path / page_path, alias_pattern)
            if page_content.original_command:
                alias_pages.append(AliasPage(page_path=page_path, content=page_content))

    return alias_pages


def prompt_alias_page_info(page_path: str) -> AliasPageContent:
    """
    Prompt user for alias page content.

    Returns:
        AliasPageContent: The collected page content
    """

    en_path = config.root / "pages"
    if not page_path.lower().endswith(".md"):
        page_path = f"{page_path}.md"
    exists = (en_path / page_path).exists()

    print(f"\n{'Updating' if exists else 'Creating new'} alias page...")
    print(create_colored_line(Colors.CYAN, f"Page path: {page_path}"))

    print(
        create_colored_line(
            Colors.BLUE,
            "\nThe title will be used in the first line of the page after '#'",
        )
    )
    print(create_colored_line(Colors.GREEN, "Example: npm run-script"))
    title = input(create_colored_line(Colors.CYAN, "Enter page title: ")).strip()
    if not title:
        raise SystemExit(create_colored_line(Colors.RED, "Title cannot be empty"))

    print(
        create_colored_line(
            Colors.BLUE,
            "\nThe original command will appear in 'This command is an alias of `command`'",
        )
    )
    print(create_colored_line(Colors.GREEN, "Example: npm run"))
    original_command = input(
        create_colored_line(Colors.CYAN, "Enter original command: ")
    ).strip()
    if not original_command:
        raise SystemExit(
            create_colored_line(Colors.RED, "Original command cannot be empty")
        )

    print(
        create_colored_line(
            Colors.BLUE,
            "\nThe documentation command will be used in 'tldr command' line",
        )
    )
    print(create_colored_line(Colors.GREEN, "Example: npm run"))
    documentation_command = input(
        create_colored_line(
            Colors.CYAN,
            f"Enter documentation command (press Enter to use {original_command}): ",
        )
    ).strip()

    if not documentation_command:
        documentation_command = original_command

    print("\nSummary:")
    print(f"* Title: {create_colored_line(Colors.CYAN, title)}")
    print(f"* Original command: {create_colored_line(Colors.CYAN, original_command)}")
    print(
        f"* Documentation command: {create_colored_line(Colors.CYAN, documentation_command)}"
    )

    print(create_colored_line(Colors.BLUE, "\nThis will create a page like:"))
    print(create_colored_line(Colors.GREEN, f"# {title}"))
    print(
        create_colored_line(
            Colors.GREEN, f"\n> This command is an alias of `{original_command}`."
        )
    )
    print(
        create_colored_line(
            Colors.GREEN, "\n- View documentation for the original command:"
        )
    )
    print(create_colored_line(Colors.GREEN, f"\n`tldr {documentation_command}`"))

    response = (
        input(create_colored_line(Colors.CYAN, "\nProceed? [Y/n] ")).lower().strip()
    )
    if response and response not in ["y", "yes"]:
        raise SystemExit(create_colored_line(Colors.RED, "Cancelled by user"))

    return AliasPageContent(
        title=title,
        original_command=original_command,
        documentation_command=documentation_command,
    )


def main():
    parser = create_argument_parser(
        "Sets the alias page for all translations of a page"
    )
    args = parser.parse_args()
    root = get_tldr_root()
    pages_dirs = get_pages_dir(root)
    templates = get_templates(root)

    global config
    config = Config(
        root=root,
        pages_dirs=pages_dirs,
        templates=templates,
        dry_run=args.dry_run,
        language=args.language,
    )

    target_paths = []

    # Use '--page' option
    if args.page != "":
        page_info = prompt_alias_page_info(args.page)
        target_paths += get_target_paths(
            args.page, config.pages_dirs, check_exists=False
        )

        for path in target_paths:
            rel_path = "/".join(path.parts[-3:])
            status = set_alias_page(path, page_info)
            if status != "":
                print(create_colored_line(Colors.GREEN, f"{rel_path} {status}"))

    # Use '--sync' option
    elif args.sync:
        en_path = config.root / "pages"
        pages_dirs = config.pages_dirs.copy()
        pages_dirs.remove(en_path)

        alias_pages = get_english_alias_pages(en_path)

        for alias_page in alias_pages:
            for pages_dir in pages_dirs:
                target_paths.extend(sync_alias_page_to_locale(pages_dir, alias_page))

    # Use '--stage' option
    if args.stage and not config.dry_run and len(target_paths) > 0:
        stage(target_paths)


if __name__ == "__main__":
    main()