"""
P8s TypeScript Type Generation.

Converts Pydantic/SQLModel models to TypeScript interfaces.
"""

import inspect
from datetime import datetime
from pathlib import Path
from typing import Any, Union, get_args, get_origin, get_type_hints
from uuid import UUID

from pydantic import BaseModel
from sqlmodel import SQLModel

# Python to TypeScript type mapping
TYPE_MAP = {
    str: "string",
    int: "number",
    float: "number",
    bool: "boolean",
    bytes: "string",
    datetime: "string",  # ISO format
    UUID: "string",
    list: "Array",
    dict: "Record<string, any>",
    Any: "any",
    None: "null",
    type(None): "null",
}


def python_type_to_typescript(python_type: Any) -> str:
    """
    Convert a Python type annotation to TypeScript.

    Args:
        python_type: Python type annotation.

    Returns:
        TypeScript type string.
    """
    # Handle None
    if python_type is None or python_type is type(None):
        return "null"

    # Handle basic types
    if python_type in TYPE_MAP:
        return TYPE_MAP[python_type]

    # Handle generic types
    origin = get_origin(python_type)
    args = get_args(python_type)

    # Handle Union (including Optional)
    if origin is Union:
        # Filter out NoneType for Optional
        non_none_args = [a for a in args if a is not type(None)]
        null_included = len(non_none_args) < len(args)

        ts_types = [python_type_to_typescript(a) for a in non_none_args]

        if null_included:
            ts_types.append("null")

        return " | ".join(ts_types)

    # Handle list
    if origin is list:
        if args:
            inner_type = python_type_to_typescript(args[0])
            return f"Array<{inner_type}>"
        return "Array<any>"

    # Handle dict
    if origin is dict:
        if len(args) >= 2:
            key_type = python_type_to_typescript(args[0])
            value_type = python_type_to_typescript(args[1])
            return f"Record<{key_type}, {value_type}>"
        return "Record<string, any>"

    # Handle Pydantic/SQLModel classes
    if inspect.isclass(python_type):
        if issubclass(python_type, BaseModel | SQLModel):
            return python_type.__name__

    # Fallback
    return "any"


def model_to_typescript(model: type[BaseModel | SQLModel]) -> str:
    """
    Convert a Pydantic/SQLModel class to TypeScript interface.

    Args:
        model: The model class.

    Returns:
        TypeScript interface definition.
    """
    interface_name = model.__name__
    fields = []

    try:
        hints = get_type_hints(model)
    except Exception:
        hints = {}

    for field_name, field_info in model.model_fields.items():
        # Get type from hints or annotation
        if field_name in hints:
            field_type = hints[field_name]
        else:
            field_type = field_info.annotation

        ts_type = python_type_to_typescript(field_type)

        # Check if optional
        is_optional = not field_info.is_required()
        optional_mark = "?" if is_optional else ""

        # Add description as comment
        description = field_info.description or ""
        comment = f"  /** {description} */\n" if description else ""

        fields.append(f"{comment}  {field_name}{optional_mark}: {ts_type};")

    fields_str = "\n".join(fields)

    return f"""export interface {interface_name} {{
{fields_str}
}}"""


def generate_typescript_types(
    models: list[type[BaseModel | SQLModel]],
    output_path: Path | str,
    *,
    header: str | None = None,
) -> None:
    """
    Generate TypeScript types from Python models.

    Args:
        models: List of Pydantic/SQLModel classes.
        output_path: Output file path.
        header: Optional header comment.

    Example:
        ```python
        from p8s.frontend import generate_typescript_types
        from myapp.models import Product, User

        generate_typescript_types(
            [Product, User],
            "frontend/src/types/models.ts"
        )
        ```
    """
    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    lines = []

    # Header
    if header:
        lines.append(f"/* {header} */")
    else:
        lines.append("/* Auto-generated by P8s - DO NOT EDIT */")

    lines.append("")

    # Generate interfaces
    for model in models:
        interface = model_to_typescript(model)
        lines.append(interface)
        lines.append("")

    # Write file
    content = "\n".join(lines)
    output_path.write_text(content)


def generate_api_client(
    app: Any,
    output_path: Path | str,
) -> None:
    """
    Generate TypeScript API client from FastAPI app.

    Args:
        app: FastAPI application instance.
        output_path: Output file path.
    """
    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    # Extract routes from app
    routes = []

    for route in app.routes:
        if hasattr(route, "methods") and hasattr(route, "path"):
            for method in route.methods:
                if method in ["GET", "POST", "PUT", "PATCH", "DELETE"]:
                    routes.append(
                        {
                            "method": method.lower(),
                            "path": route.path,
                            "name": route.name or route.path.replace("/", "_"),
                        }
                    )

    # Generate client code
    client_code = """/* Auto-generated by P8s - DO NOT EDIT */

const API_BASE = '';

async function request<T>(
  method: string,
  path: string,
  options?: RequestInit
): Promise<T> {
  const response = await fetch(`${API_BASE}${path}`, {
    method,
    headers: {
      'Content-Type': 'application/json',
      ...options?.headers,
    },
    ...options,
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  return response.json();
}

export const api = {
"""

    for route in routes:
        method = route["method"]
        path = route["path"]
        name = route["name"].replace("-", "_").replace("{", "").replace("}", "")

        # Convert path params
        if "{" in path:
            params = [p.strip("{}") for p in path.split("/") if p.startswith("{")]
            param_args = ", ".join([f"{p}: string" for p in params])
            path_template = path.replace("{", "${")

            client_code += f"""  {name}: ({param_args}) =>
    request("{method.upper()}", `{path_template}`),
"""
        else:
            client_code += f"""  {name}: () =>
    request("{method.upper()}", "{path}"),
"""

    client_code += "};\n"

    output_path.write_text(client_code)
