#!/usr/bin/env python3
"""
Pydantic → JSON Schema 生成器
=============================
Phase 14.3: 共用型別系統

功能:
- 從 Pydantic 模型生成 JSON Schema
- 正確處理 Pydantic 2.x 的 $defs 格式
- 輸出到 packages/shared-types/schemas/

使用方式:
    cd apps/api
    python ../../scripts/generate-schemas.py

建立: 2026-03-31 (台北時區)
建立者: Claude Code (Phase 14.3)
"""

import json
import sys
from pathlib import Path
from typing import Any

# 加入 apps/api/src 到 Python path
api_src = Path(__file__).parent.parent / "apps" / "api" / "src"
sys.path.insert(0, str(api_src))

# 輸出目錄
OUTPUT_DIR = Path(__file__).parent.parent / "packages" / "shared-types" / "schemas"


def extract_and_merge_defs(schema: dict[str, Any], all_defs: dict[str, Any]) -> dict[str, Any]:
    """
    從單一模型 schema 中提取 $defs 並合併到全局 $defs
    同時修正 $ref 路徑
    """
    # 提取並合併 $defs
    if "$defs" in schema:
        for def_name, def_schema in schema["$defs"].items():
            if def_name not in all_defs:
                # 遞迴處理巢狀 $defs
                cleaned_def = extract_and_merge_defs(def_schema.copy(), all_defs)
                all_defs[def_name] = cleaned_def

    # 移除 schema 中的 $defs (已合併到全局)
    cleaned_schema = {k: v for k, v in schema.items() if k != "$defs"}

    return cleaned_schema


def fix_refs(obj: Any) -> Any:
    """遞迴修正所有 $ref 路徑從 #/$defs/X 到 #/definitions/X"""
    if isinstance(obj, dict):
        result = {}
        for key, value in obj.items():
            if key == "$ref" and isinstance(value, str):
                # 修正 ref 路徑
                result[key] = value.replace("#/$defs/", "#/definitions/")
            else:
                result[key] = fix_refs(value)
        return result
    elif isinstance(obj, list):
        return [fix_refs(item) for item in obj]
    else:
        return obj


def generate_schemas():
    """生成所有模型的 JSON Schema"""

    # 確保輸出目錄存在
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

    # 收集所有 definitions
    all_definitions: dict[str, Any] = {}
    model_count = 0

    print("📦 Approval Models:")
    # === Approval Models ===
    try:
        from models.approval import (
            ApprovalRequest,
            ApprovalRequestCreate,
            ApprovalRequestResponse,
            BlastRadius,
            DryRunCheck,
            PendingApprovalsResponse,
            RejectRequest,
            Signature,
            SignRequest,
            SignResponse,
        )

        approval_models = [
            ApprovalRequest,
            ApprovalRequestCreate,
            ApprovalRequestResponse,
            BlastRadius,
            DryRunCheck,
            PendingApprovalsResponse,
            RejectRequest,
            Signature,
            SignRequest,
            SignResponse,
        ]

        for model in approval_models:
            schema = model.model_json_schema()
            cleaned = extract_and_merge_defs(schema, all_definitions)
            all_definitions[model.__name__] = cleaned
            model_count += 1
            print(f"  ✓ {model.__name__}")

    except ImportError as e:
        print(f"  ⚠ Import failed: {e}")

    print("\n📦 Incident Models:")
    # === Incident Models ===
    try:
        from models.incident import (
            AIDecisionChain,
            Incident,
            IncidentCreate,
            IncidentOutcome,
            IncidentResponse,
            IncidentUpdate,
            Signal,
        )

        incident_models = [
            AIDecisionChain,
            Incident,
            IncidentCreate,
            IncidentOutcome,
            IncidentResponse,
            IncidentUpdate,
            Signal,
        ]

        for model in incident_models:
            schema = model.model_json_schema()
            cleaned = extract_and_merge_defs(schema, all_definitions)
            all_definitions[model.__name__] = cleaned
            model_count += 1
            print(f"  ✓ {model.__name__}")

    except ImportError as e:
        print(f"  ⚠ Import failed: {e}")

    print("\n📦 Terminal Models:")
    # === Terminal Models ===
    try:
        from models.terminal import (
            SpatialContext,
            TerminalAbortRequest,
            TerminalAbortResponse,
            TerminalIntentRequest,
            TerminalIntentResponse,
            TerminalStatusResponse,
            TerminalThoughtEvent,
        )

        terminal_models = [
            TerminalIntentRequest,
            TerminalIntentResponse,
            TerminalStatusResponse,
            TerminalAbortRequest,
            TerminalAbortResponse,
            TerminalThoughtEvent,
            SpatialContext,
        ]

        for model in terminal_models:
            schema = model.model_json_schema()
            cleaned = extract_and_merge_defs(schema, all_definitions)
            all_definitions[model.__name__] = cleaned
            model_count += 1
            print(f"  ✓ {model.__name__}")

    except ImportError as e:
        print(f"  ⚠ Import failed: {e}")

    print("\n📦 Playbook Models:")
    # === Playbook Models ===
    try:
        from models.playbook import (
            Playbook,
            PlaybookCreateRequest,
            PlaybookListResponse,
            PlaybookRecommendation,
            PlaybookResponse,
            PlaybookUpdateRequest,
            RepairStep,
            SymptomPattern,
        )

        playbook_models = [
            Playbook,
            PlaybookResponse,
            PlaybookListResponse,
            PlaybookCreateRequest,
            PlaybookUpdateRequest,
            PlaybookRecommendation,
            SymptomPattern,
            RepairStep,
        ]

        for model in playbook_models:
            schema = model.model_json_schema()
            cleaned = extract_and_merge_defs(schema, all_definitions)
            all_definitions[model.__name__] = cleaned
            model_count += 1
            print(f"  ✓ {model.__name__}")

    except ImportError as e:
        print(f"  ⚠ Import failed: {e}")

    print("\n📦 NVIDIA Models:")
    # === NVIDIA Models (ADR-036) ===
    try:
        from models.nvidia import (
            NvidiaChoice,
            NvidiaMessage,
            NvidiaResponse,
            NvidiaUsage,
            ToolCall,
            ToolDefinition,
            ToolFunction,
        )

        nvidia_models = [
            ToolFunction,
            ToolCall,
            NvidiaMessage,
            NvidiaChoice,
            NvidiaUsage,
            NvidiaResponse,
            ToolDefinition,
        ]

        for model in nvidia_models:
            schema = model.model_json_schema()
            cleaned = extract_and_merge_defs(schema, all_definitions)
            all_definitions[model.__name__] = cleaned
            model_count += 1
            print(f"  ✓ {model.__name__}")

    except ImportError as e:
        print(f"  ⚠ NVIDIA import failed: {e}")

    print("\n📦 Other Models:")
    # === CSRF Models ===
    try:
        from models.csrf import CSRFTokenResponse

        schema = CSRFTokenResponse.model_json_schema()
        cleaned = extract_and_merge_defs(schema, all_definitions)
        all_definitions["CSRFTokenResponse"] = cleaned
        model_count += 1
        print("  ✓ CSRFTokenResponse")

    except ImportError as e:
        print(f"  ⚠ CSRF import failed: {e}")

    # 修正所有 $ref 路徑
    all_definitions = fix_refs(all_definitions)

    # 組裝最終 schema
    final_schema = {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "AWOOOI API Types",
        "description": "Auto-generated from Pydantic models - DO NOT EDIT",
        "definitions": all_definitions,
    }

    # 寫入檔案
    output_file = OUTPUT_DIR / "api-types.json"
    with open(output_file, "w", encoding="utf-8") as f:
        json.dump(final_schema, f, indent=2, ensure_ascii=False)

    print(f"\n✅ Schema 生成完成: {output_file}")
    print(f"   共 {len(all_definitions)} 個型別定義")

    return final_schema


if __name__ == "__main__":
    print("🔄 開始生成 JSON Schema...\n")
    generate_schemas()
