import logging
from pathlib import Path
from typing import Union, get_args

from stripe_agent_toolkit.functions import *  # noqa: F403
from stripe_agent_toolkit.prompts import *  # noqa: F403
from stripe_agent_toolkit.schema import *  # noqa: F403
from stripe_agent_toolkit.tools import tools

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def get_type_str(arg_type):
    """Extract type name, handling Optional/Union types."""
    if hasattr(arg_type, "__origin__") and arg_type.__origin__ is Union:
        non_none = [a for a in get_args(arg_type) if a is not type(None)]
        if len(non_none) == 1:
            return non_none[0].__name__
    return arg_type.__name__ if hasattr(arg_type, "__name__") else str(arg_type)


def generate_stripe_tools(
    output_file: Path = Path("arcade_stripe") / "tools" / "stripe.py",
) -> None:
    """
    Generate the Arcade AI Stripe Toolkit file from the stripe agent toolkit definitions.
    """
    logger.info("Generating stripe tools file at %s", output_file)
    try:
        output_file.touch(exist_ok=True)
        with output_file.open("w") as f:
            f.write("""import os
from typing import Annotated, Optional

from stripe_agent_toolkit.api import StripeAPI

from arcade.sdk import ToolContext, tool

def run_stripe_tool(context: ToolContext, method_name: str, params: dict) -> str:
    \"\"\"
    Helper function that retrieves the Stripe secret key, initializes the API,
    and executes the specified method with the provided parameters.
    \"\"\"
    api_key = context.get_secret("STRIPE_SECRET_KEY")
    stripe_api = StripeAPI(secret_key=api_key, context=None)
    params = {k: v for k, v in params.items() if v is not None}
    return stripe_api.run(method_name, **params)  # type: ignore[no-any-return]

""")
            # Generate each tool function from the stripe agent toolkit
            for tool_info in tools:
                method_name = tool_info["method"]
                method = globals().get(method_name)
                if not method:
                    logger.warning("Method %s not found.", method_name)
                    continue

                args_schema = tool_info["args_schema"]
                description = tool_info["description"].strip()

                arg_names = list(args_schema.__annotations__.keys())
                arg_types = [args_schema.__annotations__[field] for field in arg_names]

                params_list = []
                for name, arg_type in zip(arg_names, arg_types, strict=False):
                    field = args_schema.model_fields[name]
                    # Check if the type annotation already includes Optional (i.e. Union[..., None])
                    is_optional_type = (
                        hasattr(arg_type, "__origin__")
                        and arg_type.__origin__ is Union
                        and type(None) in get_args(arg_type)
                    )
                    if field.is_required:
                        if is_optional_type:
                            params_list.append(
                                f"{name}: Annotated[{get_type_str(arg_type)} | None, "
                                f'"{field.description}"] = None'
                            )
                        else:
                            params_list.append(
                                f"{name}: Annotated[{get_type_str(arg_type)}, "
                                f'"{field.description}"]'
                            )
                    else:
                        default_repr = "None" if field.default is None else repr(field.default)
                        params_list.append(
                            f"{name}: Annotated[Optional[{get_type_str(arg_type)}], "
                            f'"{field.description}"] = {default_repr}'
                        )
                params_str = ", ".join(params_list)
                dict_items = ", ".join([f'"{name}": {name}' for name in arg_names])
                arcade_tool_code = (
                    f'@tool(requires_secrets=["STRIPE_SECRET_KEY"])\n'
                    f"def {method_name}(context: ToolContext, {params_str}) -> "
                    f'Annotated[str, "{description.splitlines()[0]}"]:\n'
                    f'    """{description.splitlines()[0]}"""\n'
                    f'    return run_stripe_tool(context, "{method_name}", '
                    + "{"
                    + dict_items
                    + "})\n\n"
                )
                f.write(arcade_tool_code)
    except Exception:
        logger.exception("An error occurred while generating stripe tools")
        raise


if __name__ == "__main__":
    generate_stripe_tools()
