# Copyright 2025 © BeeAI a Series of LF Projects, LLC
# SPDX-License-Identifier: Apache-2.0
import dataclasses
import os.path
from pathlib import Path

from beeai_framework.template import PromptTemplate
from beeai_framework.utils.models import AnyModel

NameMapper = tuple[str, str]


@dataclasses.dataclass
class Entry:
    source: str
    target: str
    name_mapper: NameMapper | None = None


MAPPINGS: list[Entry] = []

# Requirement Agent
MAPPINGS.extend(
    [
        Entry(source="agents/experimental/__init__.py", target="agents/requirement/__init__.py"),
        Entry(source="agents/experimental/_utils.py", target="agents/requirement/utils/__init__.py"),
        Entry(source="agents/experimental/agent.py", target="agents/requirement/agent.py"),
        Entry(source="agents/experimental/events.py", target="agents/requirement/events.py"),
        Entry(source="agents/experimental/prompts.py", target="agents/requirement/prompts.py"),
        Entry(source="agents/experimental/types.py", target="agents/requirement/types.py"),
        Entry(source="agents/experimental/utils/__init__.py", target="agents/requirement/utils/__init__.py"),
        Entry(source="agents/experimental/utils/_llm.py", target="agents/requirement/utils/_llm.py"),
        Entry(source="agents/experimental/utils/_tool.py", target="agents/requirement/utils/_tool.py"),
        Entry(
            source="agents/experimental/requirements/__init__.py", target="agents/requirement/requirements/__init__.py"
        ),
        Entry(source="agents/experimental/requirements/_utils.py", target="agents/requirement/requirements/_utils.py"),
        Entry(
            source="agents/experimental/requirements/ask_permission.py",
            target="agents/requirement/requirements/ask_permission.py",
        ),
        Entry(
            source="agents/experimental/requirements/conditional.py",
            target="agents/requirement/requirements/conditional.py",
        ),
        Entry(source="agents/experimental/requirements/events.py", target="agents/requirement/requirements/events.py"),
        Entry(
            source="agents/experimental/requirements/requirement.py",
            target="agents/requirement/requirements/requirement.py",
        ),
    ]
)

# BeeAI Platform -> AgentStack
MAPPINGS.extend(
    [
        Entry(
            source=f"adapters/beeai_platform/{path}",
            target=f"adapters/agentstack/{path}",
            name_mapper=("BeeAIPlatform", "AgentStack"),
        )
        for path in [
            "__init__.py",
            "context.py",
            "agents/__init__.py",
            "agents/agent.py",
            "agents/events.py",
            "agents/types.py",
            "backend/__init__.py",
            "backend/chat.py",
            "serve/__init__.py",
            "serve/_dummy_context_store.py",
            "serve/factories.py",
            "serve/server.py",
            "serve/types.py",
            "serve/utils.py",
        ]
    ]
)


def to_import_path(path: str) -> str:
    base, ext = os.path.splitext(path)
    if os.path.basename(base) == "__init__":
        base = os.path.dirname(base)

    # Normalize filesystem separators into dots
    dotted = base.replace(os.sep, ".").replace("\\", ".")

    # Handle leading './' or similar
    dotted = dotted.lstrip(".")

    return dotted


SHIM_HEADER = "# This file is auto-generated by scripts/generate_shims.py"


def create_shim(*, old_module: str, new_module: str, name_mapper: NameMapper | None) -> str:
    template = PromptTemplate(
        schema=AnyModel,
        template="""{{header}}

import warnings
import {{new_module}} as _new_module
{{#name_mapper}}
from typing import Any
{{/name_mapper}}
{{^name_mapper}}
import sys
{{/name_mapper}}

warnings.warn(
    "{{old_module}} is deprecated and will be removed in a future release. " # noqa: E501
    "Please use {{new_module}} instead.",
    DeprecationWarning,
    stacklevel=2,
)

{{#name_mapper}}
def __getattr__(name: str) -> Any:
    final_name = name.replace("{{from_name}}", "{{to_name}}")
    if hasattr(_new_module, final_name) and final_name != name:
        warnings.warn(
            f"The imported entry '{name}' has been renamed to '{final_name}'.",
            DeprecationWarning,
            stacklevel=2,
        )
    return getattr(_new_module, final_name)
{{/name_mapper}}
{{^name_mapper}}
sys.modules[__name__] = _new_module
{{/name_mapper}}""",
    )

    return template.render(
        header=SHIM_HEADER,
        new_module=new_module,
        old_module=old_module,
        name_mapper={"from_name": name_mapper[0], "to_name": name_mapper[1]} if name_mapper else None,
    )


def main() -> None:
    root = Path(__file__).parent.parent
    project_name = "beeai_framework"

    for mapping in MAPPINGS:
        _new_path, _old_path = mapping.target, mapping.source

        new = to_import_path(f"{project_name}/{_new_path}")
        new_path = Path(root, project_name, _new_path)
        if not new_path.exists():
            raise FileNotFoundError(f"File {new_path} does not exist")

        old_path = Path(root, project_name, _old_path)
        old = to_import_path(f"{project_name}/{_old_path}")

        if old_path.exists():
            content = old_path.read_text()
            if SHIM_HEADER in content:
                print(f"Skipping shim: {old} -> {new}")
                continue

        print(f"Generating shim: {old} -> {new}")
        code = create_shim(old_module=old, new_module=new, name_mapper=mapping.name_mapper)
        old_path.parent.mkdir(parents=True, exist_ok=True)
        old_path.write_text(code)


if __name__ == "__main__":
    main()
