# pylint: disable=protected-access

import asyncio
import glob
import os
import re
import sys
from datetime import datetime
from itertools import chain
from pathlib import Path
from string import Template

import carb.tokens
import omni.ext
import omni.kit.app

from . import ext_controller
from .ext_export_import import _ask_user_for_path
from .utils import open_in_vscode_if_enabled, show_ok_popup, show_user_input_popup


def substitute_tokens_in_file(path, tokens):
    try:
        with open(path, "r", encoding="utf-8") as f:
            content = Template(f.read()).safe_substitute(tokens)
    except UnicodeDecodeError:
        # Non-text file, ignore
        return
    with open(path, "w", encoding="utf-8") as f:
        f.write(content)


def is_subpath(path, root):
    return root in path.parents


def _copy_template(src, dst, ext_id, python_module_path):
    import shutil
    from distutils.dir_util import copy_tree  # noqa: PLW0612, PLW4901
    from os.path import join

    copy_tree(join(src, "tools"), join(dst, "tools"))
    copy_tree(join(src, "vscode"), join(dst, ".vscode"))
    shutil.copy(join(src, "README.md"), join(dst, "README.md"))
    shutil.copy(join(src, "gitignore"), join(dst, ".gitignore"))
    shutil.copy(join(src, "link_app.bat"), join(dst, "link_app.bat"))
    shutil.copy(join(src, "link_app.sh"), join(dst, "link_app.sh"))
    copy_tree(join(src, "exts", "[ext_id]", "[python_module]"), join(dst, "exts", ext_id, python_module_path))
    copy_tree(join(src, "exts", "[ext_id]", "config"), join(dst, "exts", ext_id, "config"))
    copy_tree(join(src, "exts", "[ext_id]", "docs"), join(dst, "exts", ext_id, "docs"))
    copy_tree(join(src, "exts", "[ext_id]", "data"), join(dst, "exts", ext_id, "data"))


async def _create_new_extension_template():
    export_path = await _ask_user_for_path(
        is_export=True, apply_button_label="Select", title="Choose a location for your extension project"
    )
    if not export_path:
        return
    project_folder = await show_user_input_popup(
        "Name the extension project. This is the root folder of the repository.", "Project Name: ", "kit-exts-project"
    )
    export_path = os.path.join(export_path, project_folder)

    # Create folder
    os.makedirs(export_path, exist_ok=True)

    if len(os.listdir(export_path)) > 0:
        await show_ok_popup("Error", f"Folder already exist: {export_path}")
        return

    template_path = carb.tokens.get_tokens_interface().resolve("${omni.kit.window.extensions}/ext_template")

    if sys.platform == "win32":
        kit_exec = "kit.exe"
        python_exe = "python.exe"
    else:
        kit_exec = "kit"
        python_exe = "bin/python3"

    ext_id = await show_user_input_popup("Choose Extension Name:", "ext_id: ", "company.hello.world")
    ext_name = "".join([token.capitalize() for token in ext_id.split(".")])
    ext_title = " ".join(ext_id.split("."))

    ext_id_pattern = r"^[a-zA-Z0-9._]+$"
    if not re.match(ext_id_pattern, ext_id):
        await show_ok_popup("Error", f"Invalid extension name: {ext_id}. Supported chars: {ext_id_pattern}")
        return

    python_module = ext_id
    python_module_path = ext_id.replace(".", "/")

    # copy template folder
    _copy_template(template_path, export_path, ext_id, python_module_path)

    # Find app root
    def _look_for_app_root(path):
        path = Path(path)
        for p in chain([path], path.parents):
            for kit_subpath in ["", "kit"]:
                if p.joinpath(kit_subpath, kit_exec).is_file():
                    return str(p), str(kit_subpath)
        return None, None

    app_path = carb.tokens.get_tokens_interface().resolve("${app}")
    app_root_path, kit_subpath = _look_for_app_root(app_path)
    if not app_root_path:
        await show_ok_popup("Error", f"Can't find app root in {app_root_path}")
        return

    # build vscode exts search paths:
    ext_relative_paths = []
    app = omni.kit.app.get_app()
    manager = app.get_extension_manager()
    for folder in manager.get_folders():
        path_type = folder["type"]
        if path_type == omni.ext.ExtensionPathType.COLLECTION:
            path = Path(folder["path"])
            if is_subpath(path, Path(app_root_path)):
                for p in glob.glob(f"{path}/*"):
                    if os.path.isdir(p):
                        s = '        "${{workspaceFolder}}/app/{}",'.format(
                            Path(p).relative_to(app_root_path).as_posix()
                        )
                        ext_relative_paths.append(s)

    export_exts_path = f"{export_path}/exts"
    export_app_path = f"{export_path}/app"

    # substitute all tokens
    tokens = {
        "ext_id": ext_id,
        "ext_name": ext_name,
        "ext_title": ext_title,
        "kit_subpath": kit_subpath + "/" if kit_subpath else "",
        "python_exe": python_exe,
        "python_module": python_module,
        "ext_search_paths": "\n".join(ext_relative_paths),
        "export_path": export_path,
        "export_exts_path": export_exts_path,
        "export_app_path": export_app_path,
        "author": os.environ.get("USERNAME", "please fill in author information"),
        "today": datetime.today().strftime("%Y-%m-%d"),
    }
    for folder, _, files in os.walk(export_path):
        for filename in files:
            substitute_tokens_in_file(os.path.join(folder, filename), tokens)

    # create link
    omni.ext.create_link(export_app_path, app_root_path)

    # try open in vscode:
    open_in_vscode_if_enabled(export_path, prefer_vscode=True)

    # add new search path (remove + add will trigger fs search for sure)
    manager.remove_path(export_exts_path)
    manager.add_path(export_exts_path, omni.ext.ExtensionPathType.COLLECTION_USER)

    await app.next_update_async()

    # Enable that one ext and make it autoload
    manager.set_extension_enabled(ext_id, True)
    ext_controller.toggle_autoload(f"{ext_id}-1.0.0", True)

    # Focus on it
    from .extension import get_instance

    instance = get_instance()()
    instance._window._ext_info_widget._refresh_once_next_frame()
    instance._window._exts_list_widget._search.set_text(ext_id)


def create_new_extension_template():
    asyncio.ensure_future(_create_new_extension_template())
