"""
Material Detail API Handler

职责：
- 加载物资明细列表（依赖 Material）
- 物资明细创建/更新/删除

实现的接口：
GET:
- /material_detail/list - 获取物资明细列表

POST:
- /material_detail/create - 创建物资明细

PUT:
- /material_detail/update - 更新物资明细

DELETE:
- /material_detail/delete - 删除物资明细
"""

from typing import List, Dict, Any
from pydantic import ValidationError

from ...datasource.api.handler import BaseApiHandler
from ...datasource.task_result import TaskResult
from ...common.sync_node import SyncNode
from ...datasource.api.errors import ERROR_NODE_DATA_NONE, ERROR_VALIDATION_FAILED, ERROR_ID_NOT_FOUND
from .sync_node import MaterialDetailSyncNode
from schemas.material.material_detail import (
    MaterialDetailResponse,
    MaterialDetailCreate,
    MaterialDetailUpdate
)


class MaterialDetailApiHandler(BaseApiHandler):
    """物资明细 API Handler"""
    
    def __init__(self):
        super().__init__()
        self._node_type = "material_detail"
        self._node_class = MaterialDetailSyncNode
        self._schema = MaterialDetailResponse
        self.update_schemas = [MaterialDetailCreate, MaterialDetailUpdate]
    
    async def load(self) -> List[SyncNode]:
        """加载物资明细列表（依赖 Material）"""
        try:
            materials = self._collection.filter(node_type="material")
            material_ids = [m.data_id for m in materials if m.data_id]
            
            if not material_ids:
                return []
            
            all_nodes = []
            for material_id in material_ids:
                try:
                    details = await api_get_material_detail_list(self.api_client, material_id)
                    for detail_model in details:
                        # 已验证的 Pydantic model，转换为 dict
                        detail_data = detail_model.model_dump()
                        node = self._create_node(detail_data)
                        all_nodes.append(node)
                
                except Exception as e:
                    print(f"❌ Failed to load material details for {material_id}: {e}")
            
            return all_nodes
        except Exception as e:
            print(f"❌ Failed to load material details: {e}")
            return []
    
    async def create_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """批量创建物资明细"""
        results = []
        for node in nodes:
            try:
                data = node.get_data()
                if not data:
                    results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_NODE_DATA_NONE))
                    continue
                
                # 从data的parent_id获取material_id
                material_id = data.get("parent_id")
                
                if not material_id:
                    results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_ID_NOT_FOUND))
                    continue
                
                # 验证并转换为 Pydantic model
                try:
                    create_data = {"material_id": material_id, **data}
                    create_model = MaterialDetailCreate.model_validate(create_data)
                except ValidationError as e:
                    results.append(TaskResult.failed(node_id=node.node_id, error=f"{ERROR_VALIDATION_FAILED}: {e}"))
                    continue
                
                push_id = self._generate_push_id()
                
                response = await api_create_material_detail(
                    self.api_client,
                    create_model,
                    push_id,
                    steps=self._get_request_steps(node),
                )
                if self.poll_mode == "sync":
                    created_id = self._extract_created_id_from_response(response)
                    if not created_id:
                        results.append(
                            TaskResult.failed(
                                node_id=node.node_id,
                                error="Missing created_id in create response"
                            )
                        )
                        continue
                    results.append(TaskResult.success(node_id=node.node_id, data_id=created_id))
                else:
                    results.append(TaskResult.in_progress(node_id=node.node_id, task_id=push_id))
            except Exception as e:
                results.append(TaskResult.failed(node_id=node.node_id, error=f"Create failed: {str(e)}"))
        
        return results
    
    async def update_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """
        批量更新物资明细
        
        按 material_id 分组，每组使用同一个 push_id 批量提交
        """
        from collections import defaultdict
        
        results = []
        
        # 按 material_id 分组
        groups: Dict[str, List[tuple[SyncNode, Dict[str, Any]]]] = defaultdict(list)
        
        for node in nodes:
            try:
                data = node.get_data()
                if not data:
                    results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_NODE_DATA_NONE))
                    continue
                
                origin_data = node.get_origin_data() or {}
                
                # 使用 get_update_schema_diff 作为 update 入口检查差异并打印日志
                force_steps_update = self._steps_changed(node)
                diff_fields = self.get_update_schema_diff(
                    MaterialDetailUpdate,
                    data,
                    origin_data,
                    node.node_id,
                    force_full_schema=force_steps_update,
                )
                if not diff_fields:
                    from ...common.types import SyncAction
                    results.append(TaskResult.skipped(node_id=node.node_id, reason="No updatable fields changed"))
                    continue
                
                # 获取 material_id（parent_id）
                material_id = data.get("parent_id") or node.context.get("material_id")
                if not material_id:
                    results.append(TaskResult.failed(node_id=node.node_id, error="Missing material_id (parent_id)"))
                    continue
                
                # 需要完整的 schema 数据（包括必填字段）
                update_data = self.build_update_request_data(
                    data,
                    origin_data,
                    MaterialDetailUpdate,
                    force_full_schema=force_steps_update,
                )
                
                # 验证并转换为 Pydantic model
                try:
                    update_model = MaterialDetailUpdate.model_validate(update_data)
                    groups[material_id].append((node, update_model))
                except ValidationError as e:
                    results.append(TaskResult.failed(node_id=node.node_id, error=f"{ERROR_VALIDATION_FAILED}: {e}"))
            except Exception as e:
                results.append(TaskResult.failed(node_id=node.node_id, error=f"Update failed: {str(e)}"))
        
        # 逐组批量更新
        for material_id, group_items in groups.items():
            push_id = self._generate_push_id()
            
            # 准备批量数据
            detail_list = [model.model_dump(mode='json', exclude_unset=True) for node, model in group_items]
            steps = self._get_request_steps(group_items[0][0]) if group_items else []
            
            # 使用 parent_id（material_id）作为 biz_id
            biz_id = material_id
            
            try:
                # 批量调用 API
                await api_update_material_detail(
                    self.api_client,
                    {"detail_list": detail_list},
                    push_id,
                    biz_id,
                    steps=steps,
                )
                # 整组成功
                for node, _ in group_items:
                    self._mark_approval_snapshot_synced(node)
                    results.append(TaskResult.in_progress(node_id=node.node_id, task_id=push_id))
            except Exception as e:
                # 整组失败
                error_msg = str(e)
                for node, _ in group_items:
                    results.append(TaskResult.failed(node_id=node.node_id, error=error_msg))
        
        return results
    
    async def delete_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """批量删除物资明细"""
        results = []
        for node in nodes:
            try:
                push_id = self._generate_push_id()
                biz_id = node.data_id
                if not biz_id:
                    results.append(TaskResult.failed(node_id=node.node_id, error="Missing biz_id for delete"))
                    continue
                
                await api_delete_material_detail(
                    self.api_client,
                    push_id,
                    biz_id
                )
                results.append(TaskResult.success(node_id=node.node_id))
            except Exception as e:
                results.append(TaskResult.failed(node_id=node.node_id, error=f"Delete failed: {str(e)}"))
        
        return results
    
    async def poll_tasks(self, task_ids: List[str]) -> Dict[str, TaskResult]:
        """轮询异步任务状态"""
        return await super().poll_tasks(task_ids)
    
    def _create_node(self, data: Dict[str, Any]) -> SyncNode:
        """创建物资明细节点，设置 material_id 到 context"""
        node = self._create_basic_node(data, depend_ids=[])
        
        if data.get("material_id"):
            node.context["material_id"] = data["material_id"]
        
        return node


# ========== 静态 API 函数 ==========

async def api_get_material_detail_list(client, material_id: str) -> List[MaterialDetailResponse]:
    """
    获取物资明细列表 GET /material_detail/list
    
    Args:
        client: API 客户端
        material_id: 物资ID
        
    Returns:
        List[MaterialDetailResponse]: 验证过的明细列表
    """
    from pydantic import ValidationError
    
    response = await client.request(
        method="GET",
        endpoint="/material/detail/list",
        params={"material_id": material_id}
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
    
    items = response.get("data", [])
    
    # 验证每个返回项
    validated_items = []
    for item in items:
        try:
            validated_item = MaterialDetailResponse.model_validate(item)
            validated_items.append(validated_item)
        except ValidationError as e:
            print(f"⚠️ Skipping invalid material detail item: {e}")
            continue
    
    return validated_items


async def api_create_material_detail(
    client,
    data: MaterialDetailCreate,
    push_id: str,
    *,
    steps: List[Dict[str, Any]] | None = None,
) -> Dict[str, Any]:
    """
    创建物资明细 POST /material_detail/create
    
    Args:
        client: API 客户端
        data: 创建数据（Pydantic model）
        push_id: 推送ID
        biz_id: 业务ID
    """
    request_data = {"push_id": push_id, "data": data.model_dump(mode='json'), "steps": steps or []}
    response = await client.request(
        method="POST",
        endpoint="/material/detail/create",
        data=request_data
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
    return response


async def api_update_material_detail(
    client,
    data: Dict[str, Any],
    push_id: str,
    biz_id: str,
    *,
    steps: List[Dict[str, Any]] | None = None,
):
    """
    更新物资明细 PUT /material_detail/update
    
    Args:
        client: API 客户端
        data: 更新数据（Pydantic model）
        push_id: 推送ID
        biz_id: 业务ID
    """
    request_data = {
        "push_id": push_id,
        "biz_id": biz_id,
        "data": data,
        "steps": steps or [],
    }
    response = await client.request(
        method="PUT",
        endpoint="/material/detail/update",
        data=request_data
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")


async def api_delete_material_detail(client, push_id: str, biz_id: str):
    """删除物资明细"""
    request_data = {
        "push_id": push_id,
        "biz_id": biz_id
    }
    response = await client.request(
        method="DELETE",
        endpoint="/material/detail/delete",
        data=request_data
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
