#!/usr/bin/env python3

import os
import re
from argparse import ArgumentParser

# Template for default.nix
# TODO: conditionally include parts of the template based on args
default_nix_template = """{{ lib, ... }}:
lib.nixvim.plugins.mkNeovimPlugin {{
  name = "{name}";
  moduleName = "LUA_MODULE_NAME"; # TODO replace (or remove entirely if it is the same as `name`)
  packPathName = "{originalName}";
  package = "{package}";

  {maintainer_todo}maintainers = [ lib.maintainers.{maintainer} ]

  # TODO provide an example for the `settings` option (or remove entirely if there is no useful example)
  # NOTE you can use `lib.literalExpression` or `lib.literalMD` if needed
  settingsExample = {{
    foo = 42;
    bar.__raw = "function() print('hello') end";
  }};
}}
"""

# Template for test file
test_nix_template = """{{
  empty = {{
    plugins.{name}.enable = true;
  }};

  defaults = {{
    plugins.{name} = {{
      enable = true;
      settings = {{
        foo = 42;
        bar.__raw = "function() print('hello') end";
      }};
    }};
  }};
}}
"""


def to_kebab_case(input_string):
    """
    Convert a string to kebab-case.

    Args:
        input_string (str): The input string to convert.

    Returns:
        str: The converted kebab-case string.
    """
    # Replace non-alphanumeric characters with hyphens
    input_string = re.sub(r"[\W_]+", "-", input_string).lower()

    return input_string.strip("-")


def strip_nvim(input_string):
    """
    Remove 'nvim' prefix or suffix from a string.

    Args:
        input_string (str): The input string to process.

    Returns:
        str: The string with 'nvim' removed from start or end.
    """
    # Remove leading and trailing standalone 'nvim'
    input_string = re.sub(r"(^nvim-|-nvim$|^nvim$)", "", input_string)

    return input_string.strip("-")


def create_nix_file(
    file_path,
    template,
    name,
    originalName,
    package,
    maintainer,
    is_default_maintainer=False,
    dry_run=False,
):
    """
    Create a nix file from a template.

    Args:
        file_path (str): The path to the file to create.
        template (str): The template string to use for the file content.
        name (str): The name of the plugin.
        originalName (str): The original name of the plugin.
        package (str): The package name of the plugin.
        maintainer (str): The maintainer name from lib.maintainers.
        is_default_maintainer (bool): Whether the maintainer is the default value.
        dry_run (bool): If True, only print what would be written instead of actually writing.
    """
    # Add a TODO comment if using the default maintainer
    maintainer_todo = (
        "# TODO replace with your name \n  " if is_default_maintainer else ""
    )

    content = template.format(
        name=name,
        originalName=originalName,
        package=package,
        maintainer=maintainer,
        maintainer_todo=maintainer_todo,
    )
    write_to_file(file_path, content, dry_run)


def create_test_file(file_path, template, name, dry_run=False):
    """
    Create a test file from a template.

    Args:
        file_path (str): The path to the file to create.
        template (str): The template string to use for the file content.
        name (str): The name of the plugin.
        dry_run (bool): If True, only print what would be written instead of actually writing.
    """
    content = template.format(name=name)
    write_to_file(file_path, content, dry_run)


def write_to_file(file_path, content: str, dry_run=False):
    """
    Makes sure directories exist and write content to a file.

    Args:
    file_path (str): The path to the file to write.
    content (str): The content to write to the file.
    dry_run (bool): If True, only print what would be written instead of actually writing.
    """
    if dry_run:
        print(f"Would write to {file_path}:")
        print("=" * 40)
        print(content)
        print("=" * 40)
        return

    os.makedirs(os.path.dirname(file_path), exist_ok=True)
    with open(file_path, "w") as f:
        f.write(content)


def find_project_root(root_identifier):
    current_path = os.getcwd()
    while True:
        if root_identifier in os.listdir(current_path):
            return current_path
        parent_path = os.path.dirname(current_path)
        if parent_path == current_path:
            return None
        os.chdir("..")
        current_path = os.getcwd()


# TODO: support interactive unmanaged args
def main():
    """
    Main function to generate default.nix and test files for a new plugin.
    """
    DEFAULT_MAINTAINER = "YOUR_NAME"

    parser = ArgumentParser(
        description="Generate default.nix and test files for a new plugin"
    )
    parser.add_argument(
        "originalName", type=str, help="Original name of the new plugin"
    )
    parser.add_argument(
        "--package",
        "-p",
        type=str,
        help="Package name of the new plugin (defaults to normalized version of originalName)",
    )
    parser.add_argument(
        "--maintainer",
        "-m",
        type=str,
        help="Maintainer name (from lib.maintainers)",
        default=DEFAULT_MAINTAINER,
    )
    parser.add_argument(
        "--dry-run",
        "-d",
        action="store_true",
        help="Show what would be written without actually creating files",
    )
    args = parser.parse_args()

    # Calculate name - convert to kebab case and strip nvim
    name = strip_nvim(to_kebab_case(args.originalName))

    # Use provided package name or default to normalized original name
    package = args.package if args.package else to_kebab_case(args.originalName)

    # Check if user provided a maintainer or we're using the default
    is_default_maintainer = args.maintainer == DEFAULT_MAINTAINER

    # Define paths
    root_identifier = "flake.nix"
    root_dir = find_project_root(root_identifier)

    plugin_path = f"{root_dir}/plugins/by-name/{name}/default.nix"
    test_path = f"{root_dir}/tests/test-sources/plugins/by-name/{name}/default.nix"

    # Create files
    create_nix_file(
        plugin_path,
        default_nix_template,
        name,
        args.originalName,
        package,
        args.maintainer,
        is_default_maintainer,
        args.dry_run,
    )
    create_test_file(
        test_path,
        test_nix_template,
        name,
        args.dry_run,
    )


if __name__ == "__main__":
    main()