"""
Project API Handler

实现的接口：
GET:
- /project/list - 获取项目列表
- /project - 获取项目详情
- /project/all_info - 获取项目完整信息

PUT (6个更新接口):
- /project - 更新项目基本信息
- /project/key_constraints - 关键节点
- /project/complete_investment - 完成投资
- /project/investment_decision - 投资决策
- /project/dynamic_investment - 动态投资
- /project/settlement_investment - 结算投资

不支持: CREATE, DELETE
"""

import asyncio
from typing import List, Dict, Any
from ...datasource.api.handler import BaseApiHandler
from ...datasource.task_result import TaskResult
from ...common.sync_node import SyncNode
from .sync_node import ProjectSyncNode
from schemas.project.project_base import (
    ProjectResponseBase,
    ProjectBaseInfoUpdate,
    ProjectKeyConstraints,
    ProjectCompleteInvestment,
    ProjectInvestmentDecision,
    ProjectDynamicInvestment,
    ProjectSettlementInvestment
)
from schemas.project.project import ProjectInfoResponse


_PROJECT_EXTENSION_FIELD_NAMES = {
    "implementation_estimate",
    "static_total_investment",
    "completed_investment",
    "total_contract_quantity",
    "completed_settlement_quantity",
}


def _merge_project_all_info_fields(
    project_data: Dict[str, Any],
    all_info: Dict[str, Any],
) -> Dict[str, Any]:
    """把 all_info 中项目扩展字段拍平到 project 主数据上。"""
    merged = dict(project_data)

    for key, value in all_info.items():
        if not key.endswith("_extension") or not isinstance(value, dict):
            continue
        for field_name in _PROJECT_EXTENSION_FIELD_NAMES:
            if field_name not in value:
                continue
            current_value = merged.get(field_name)
            if current_value in (None, ""):
                merged[field_name] = value.get(field_name)

    return merged


class ProjectApiHandler(BaseApiHandler):
    """项目 API Handler"""
    
    # 类级别缓存：存储 all_info 数据
    _all_info_cache: Dict[str, Dict[str, Any]] = {}
    
    def __init__(self, **handler_config: Any):
        super().__init__(**handler_config)
        self._node_type = "project"
        self._node_class = ProjectSyncNode
        self._schema = ProjectResponseBase
    
    @classmethod
    async def get_all_info(cls, client, project_id: str) -> Dict[str, Any]:
        """获取项目完整信息（带缓存）
        
        Args:
            client: API client
            project_id: 项目ID
            
        Returns:
            项目完整信息字典
        """
        if project_id in cls._all_info_cache:
            return cls._all_info_cache[project_id]
        
        all_info = await api_get_project_all_info(client, project_id)
        cls._all_info_cache[project_id] = all_info
        return all_info
    
    @classmethod
    def clear_cache(cls):
        """清空缓存（每次同步开始前调用）"""
        cls._all_info_cache.clear()
    
    async def load(self) -> List[SyncNode]:
        """加载项目列表并预加载 all_info（支持分页）
        
        Args:
            project_id: 可选，指定项目ID列表则只加载这些项目（优先级高于构造函数的filter_project_id）
        """
        try:
            # 清空旧缓存
            self.clear_cache()
            
            nodes = []
            
            project_id_filter = self.get_data_id_filter()
            
            if project_id_filter:
                # 只加载指定项目列表
                projects : List[ProjectResponseBase]= []
                for project_id in project_id_filter:
                    project_response = await api_get_project(self.api_client, project_id)
                    projects.append(project_response)
            else:
                # 加载所有项目（分页）
                projects : List[ProjectResponseBase]= []
                page = 1
                page_size = 100
                
                while True:
                    # 获取一页数据
                    page_projects, total = await api_get_project_list(
                        self.api_client,
                        page=page,
                        page_size=page_size
                    )
                    
                    # 没有数据了，退出循环
                    if not page_projects:
                        break
                    
                    projects.extend(page_projects)
                    
                    # 检查是否还有下一页
                    if page * page_size >= total:
                        break
                    
                    page += 1
            
            # 处理所有项目
            for project_data in projects:
                project_item = project_data.model_dump()
                if self.should_skip_by_data_id_filter(project_item, project_data.id):
                    continue

                # 预加载并缓存 all_info，同时存储到 node.context
                all_info = await self.get_all_info(self.api_client, project_data.id)
                normalized_project_data = _merge_project_all_info_fields(
                    project_item,
                    all_info,
                )
                node = self._create_node(normalized_project_data)
                # 设置 context
                node.context["project_id"] = project_data.id
                node.context["all_info"] = all_info
                
                nodes.append(node)
            return nodes
        except Exception as e:
            # 打印详细错误信息并重新抛出，让上层处理
            print(f"\n{'='*70}")
            print("❌ 项目加载失败")
            print(f"{'='*70}")
            print(f"错误类型: {type(e).__name__}")
            print(f"错误信息: {str(e)}")
            
            print(f"{'='*70}")
            import traceback
            traceback.print_exc()
            print(f"{'='*70}\n")
            
            # 重新抛出异常，让DataSource能正确处理
            raise
    
    async def create_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """不支持创建"""
        return [
            TaskResult.failed(node_id=node.node_id, error="Project creation not supported")
            for node in nodes
        ]
    
    async def update_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """
        批量更新项目（支持多种更新类型）
        
        支持 6 种更新 schema：
        1. ProjectBaseInfoUpdate - 基础信息
        2. ProjectKeyConstraints - 关键约束
        3. ProjectCompleteInvestment - 竣工投资
        4. ProjectInvestmentDecision - 投资决策
        5. ProjectDynamicInvestment - 动态投资
        6. ProjectSettlementInvestment - 结算投资
        """
        results = []
        
        # 定义所有更新 schema 和对应的 API 函数
        update_configs = [
            (ProjectBaseInfoUpdate, api_update_project_base_info),
            (ProjectKeyConstraints, api_update_project_key_constraints),
            (ProjectCompleteInvestment, api_update_project_complete_investment),
            (ProjectInvestmentDecision, api_update_project_investment_decision),
            (ProjectDynamicInvestment, api_update_project_dynamic_investment),
            (ProjectSettlementInvestment, api_update_project_settlement_investment),
        ]
        
        for node in nodes:
            data = node.get_data()
            if not data:
                error_msg = node.error or "Node data is missing or invalid"
                results.append(TaskResult.failed(node_id=node.node_id, error=f"Cannot update project: {error_msg}"))
                continue
            
            original_data = node.get_origin_data() or {}
            biz_id = node.data_id
            if not biz_id:
                results.append(TaskResult.failed(node_id=node.node_id, error="Missing biz_id for update"))
                continue
            
            node_updated = False
            error = None
            force_steps_update = self._steps_changed(node)
            steps_payload = self._get_request_steps(node)
            steps_sent = False
            allow_force_steps = True
            
            # 遍历所有更新 schema，检查差异并更新
            for schema, api_func in update_configs:
                update_data = self._get_schema_diff(
                    schema,
                    data,
                    original_data,
                    node.node_id,
                    force_full_schema=force_steps_update and allow_force_steps,
                )
                if not update_data:
                    continue
                
                # 执行更新（差异已由 _get_schema_diff 打印）
                try:
                    full_update_data = self._filter_update_data(
                        data,
                        original_data,
                        schema,
                        force_full_schema=force_steps_update and allow_force_steps,
                    )
                    await self._submit_update_and_wait(
                        api_func=api_func,
                        data=full_update_data,
                        biz_id=biz_id,
                        steps=steps_payload if not steps_sent else [],
                    )
                    node_updated = True
                    steps_sent = steps_sent or bool(steps_payload)
                    allow_force_steps = False
                    self._mark_approval_snapshot_synced(node)
                except Exception as e:
                    error = str(e)
                    break
            
            # 汇总结果
            if error:
                results.append(TaskResult.failed(node_id=node.node_id, error=error))
            elif node_updated:
                results.append(TaskResult.success(node_id=node.node_id))
            else:
                # 没有任何字段需要更新
                reason = f"No physical diff found across all 6 sub-schemas for project {biz_id} despite strategy update request (Normalization Discrepancy)."
                print(f"  [WARNING] {reason}")
                results.append(TaskResult.skipped(node_id=node.node_id, reason=reason))
        
        return results
    
    async def delete_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
        """不支持删除"""
        return [
            TaskResult.failed(node_id=node.node_id, error="Project deletion not supported")
            for node in nodes
        ]
    
    async def poll_tasks(self, task_ids: List[str]) -> Dict[str, TaskResult]:
        """轮询异步任务状态"""
        return await super().poll_tasks(task_ids)

    async def _submit_update_and_wait(self, *, api_func, data: Dict[str, Any], biz_id: str, steps: List[Dict[str, Any]] | None = None) -> None:
        push_id = self._generate_push_id()
        await api_func(self.api_client, data, push_id, biz_id, steps=steps or [])

        max_attempts = 20
        poll_interval = 0.2
        last_result: TaskResult | None = None

        for attempt in range(max_attempts):
            polled = await super().poll_tasks([push_id])
            result = polled.get(push_id)
            last_result = result

            if result is None:
                raise RuntimeError(f"Project update poll missing result: push_id={push_id}")
            if result.status == result.status.SUCCESS:
                return
            if result.status == result.status.FAILED:
                raise RuntimeError(result.error or f"Project update failed: push_id={push_id}")
            if attempt < max_attempts - 1:
                await asyncio.sleep(poll_interval)

        raise RuntimeError(
            f"Project update poll timeout: push_id={push_id}, last_status={getattr(last_result, 'status', None)}"
        )
    
    def _create_node(self, data: Dict[str, Any]) -> SyncNode:
        node = self._create_basic_node(data, depend_ids=[])

        # 设置 context
        node.context["project_id"] = node.data_id

        return node


# ========== 静态 API 函数（可抽成 SDK） ==========

async def api_get_project_list(client, page: int = 1, page_size: int = 100) -> tuple[List[ProjectResponseBase], int]:
    """GET /project/list - 获取项目列表
    
    Args:
        client: API客户端
        page: 页码，从1开始
        page_size: 每页大小
        
    Returns:
        tuple[List[ProjectResponseBase], int]: (验证过的项目列表, 总数)
    """
    response = await client.request(
        method="GET",
        endpoint="/project/list",
        params={"page": page, "page_size": page_size}
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
    
    data = response.get("data", {})
    list_data = data.get("list", [])
    total = data.get("total", 0)
    
    # 验证每个返回项
    validated_items = []
    for item in list_data:
        validated_item = ProjectResponseBase.model_validate(item)
        validated_items.append(validated_item)
    
    return validated_items, total


async def api_get_project(client, project_id: str) -> ProjectResponseBase:
    """GET /project - 获取项目详情"""
    response = await client.request(
        method="GET",
        endpoint="/project",
        params={"project_id": project_id}
    )
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
    
    return ProjectResponseBase.model_validate(response.get("data"))


async def api_get_project_all_info(client, project_id: str) -> Dict[str, Any]:
    """GET /project/all_info - 获取项目完整信息（校验为 ProjectInfoResponse）"""
    response = await client.request(
        method="GET",
        endpoint="/project/all_info",
        params={"project_id": project_id}
    )
    
    # 检查响应状态
    code = response.get('code')
    if code != 200:
        error_msg = response.get('message', 'Unknown error')
        
        # 422 验证错误时打印详细信息
        if code == 422:
            print(f"\n{'='*70}")
            print("❌ API 422 验证错误")
            print(f"{'='*70}")
            print("接口: GET /project/all_info")
            print(f"项目ID: {project_id}")
            print(f"错误信息: {error_msg}")
            print("完整响应:")
            import json
            print(json.dumps(response, indent=2, ensure_ascii=False))
            print(f"{'='*70}\n")
        
        raise Exception(f"API error {code}: {error_msg}")
    
    data = response.get('data', {})
    validated = ProjectInfoResponse.model_validate(data)
    return validated.model_dump()


async def api_update_project_base_info(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project - 更新项目基本信息"""
    ProjectBaseInfoUpdate.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")


async def api_update_project_key_constraints(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project/key_constraints - 更新关键节点"""
    ProjectKeyConstraints.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project/key_constraints", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")


async def api_update_project_complete_investment(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project/complete_investment - 更新完成投资"""
    ProjectCompleteInvestment.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project/complete_investment", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")


async def api_update_project_investment_decision(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project/investment_decision - 更新投资决策"""
    ProjectInvestmentDecision.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project/investment_decision", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")


async def api_update_project_dynamic_investment(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project/dynamic_investment - 更新动态投资"""
    ProjectDynamicInvestment.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project/dynamic_investment", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")




async def api_update_project_settlement_investment(client, data: Dict[str, Any], push_id: str, biz_id: str, *, steps: List[Dict[str, Any]] | None = None) -> None:
    """PUT /project/settlement_investment - 更新结算投资"""
    ProjectSettlementInvestment.model_validate(data)
    request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
    response = await client.request(method="PUT", endpoint="/project/settlement_investment", data=request_data)
    if response.get("code") != 200:
        raise Exception(f"API error: {response.get('message', 'Unknown error')}")
