"""
engine.instance — Agent Instance 管理

Agent 是函数定义，Instance 是函数调用。
一份 agent 代码（模板）可以被不同领域的数据实例化多次。

目录结构:
    agentexample/qaagent67wiki/          ← agent 模板（版本控制）
        agent-config.yml
        agents/, skills/, scripts/

    /data/instances/maritime/            ← 实例数据（不版本控制）
        instance.yml                     ← 覆盖配置（指向 agent 模板 + 路径覆盖）
        wiki/                            ← 累积知识
        knowledge/                       ← 原始文件 + 索引
        workspace/                       ← per-run 数据

用法:
    config = load_instance("/data/instances/maritime/instance.yml")
    # config = agent-config.yml 的内容，被 instance.yml 覆盖后的合并结果

    # 或从 agent_dir + instance_dir 分别指定:
    config = load_instance_from_dirs(
        agent_dir="agentexample/qaagent67wiki",
        instance_dir="/data/instances/maritime",
    )
"""
from __future__ import annotations

import copy
import os
import time
from typing import Any, Dict, Optional

import yaml


def _deep_merge(base: dict, override: dict) -> dict:
    """
    深度合并两个 dict。override 的值覆盖 base 的值。
    嵌套 dict 递归合并，非 dict 值直接覆盖。
    """
    result = copy.deepcopy(base)
    for key, val in override.items():
        if key in result and isinstance(result[key], dict) and isinstance(val, dict):
            result[key] = _deep_merge(result[key], val)
        else:
            result[key] = copy.deepcopy(val)
    return result


def load_instance(instance_path: str) -> Dict[str, Any]:
    """
    从 instance.yml 加载合并后的配置。

    instance.yml 必须包含 `agent` 字段指向 agent 模板目录。
    其余字段深度合并覆盖 agent-config.yml。

    Args:
        instance_path: instance.yml 的路径

    Returns:
        合并后的完整配置 dict，额外包含:
            _agent_dir: agent 模板目录绝对路径
            _instance_dir: 实例数据目录绝对路径
    """
    instance_dir = os.path.dirname(os.path.abspath(instance_path))

    with open(instance_path, "r", encoding="utf-8") as f:
        instance_cfg = yaml.safe_load(f) or {}

    # agent 字段: 指向 agent 模板
    agent_ref = instance_cfg.pop("agent", None)
    if not agent_ref:
        raise ValueError(f"instance.yml must have 'agent' field: {instance_path}")

    # 解析 agent 模板目录
    agent_dir = _resolve_agent_dir(agent_ref, instance_dir)
    agent_config_path = os.path.join(agent_dir, "agent-config.yml")

    if not os.path.isfile(agent_config_path):
        raise FileNotFoundError(
            f"Agent template not found: {agent_config_path} "
            f"(referenced by agent: {agent_ref})"
        )

    with open(agent_config_path, "r", encoding="utf-8") as f:
        agent_cfg = yaml.safe_load(f) or {}

    # 从 instance_cfg 中剥离元数据字段
    instance_name = instance_cfg.pop("name", "")
    instance_cfg.pop("description", "")

    # 深度合并: agent_cfg 为 base, instance_cfg 为 override
    merged = _deep_merge(agent_cfg, instance_cfg)

    # 注入元数据
    merged["_agent_dir"] = agent_dir
    merged["_instance_dir"] = instance_dir
    if instance_name:
        merged["_instance_name"] = instance_name

    return merged


def load_instance_from_dirs(
    agent_dir: str,
    instance_dir: str,
) -> Dict[str, Any]:
    """
    从分别指定的 agent_dir 和 instance_dir 加载合并配置。

    如果 instance_dir 下有 instance.yml，用它覆盖 agent-config.yml。
    如果没有 instance.yml，直接使用 agent-config.yml。

    Args:
        agent_dir: agent 模板目录
        instance_dir: 实例数据目录

    Returns:
        合并后的配置 dict
    """
    agent_dir = os.path.abspath(agent_dir)
    instance_dir = os.path.abspath(instance_dir)

    agent_config_path = os.path.join(agent_dir, "agent-config.yml")
    if not os.path.isfile(agent_config_path):
        raise FileNotFoundError(f"Agent config not found: {agent_config_path}")

    with open(agent_config_path, "r", encoding="utf-8") as f:
        agent_cfg = yaml.safe_load(f) or {}

    # 尝试加载 instance.yml
    instance_config_path = os.path.join(instance_dir, "instance.yml")
    if os.path.isfile(instance_config_path):
        with open(instance_config_path, "r", encoding="utf-8") as f:
            instance_cfg = yaml.safe_load(f) or {}
        # 剥离元数据
        instance_cfg.pop("agent", None)
        instance_name = instance_cfg.pop("name", "")
        instance_cfg.pop("description", "")
        merged = _deep_merge(agent_cfg, instance_cfg)
        if instance_name:
            merged["_instance_name"] = instance_name
    else:
        merged = agent_cfg

    merged["_agent_dir"] = agent_dir
    merged["_instance_dir"] = instance_dir

    return merged


def create_instance(
    instance_dir: str,
    agent_ref: str,
    name: str = "",
    overrides: Dict[str, Any] | None = None,
) -> str:
    """
    创建新的 agent 实例。

    在 instance_dir 下创建:
        instance.yml
        wiki/
        knowledge/raw/
        knowledge/processed/
        workspace/

    Args:
        instance_dir: 实例目录路径
        agent_ref: agent 模板引用（目录名或路径）
        name: 实例名称
        overrides: 额外配置覆盖

    Returns:
        instance.yml 的路径
    """
    os.makedirs(instance_dir, exist_ok=True)
    os.makedirs(os.path.join(instance_dir, "wiki"), exist_ok=True)
    os.makedirs(os.path.join(instance_dir, "knowledge", "raw"), exist_ok=True)
    os.makedirs(os.path.join(instance_dir, "knowledge", "processed"), exist_ok=True)
    os.makedirs(os.path.join(instance_dir, "workspace"), exist_ok=True)

    instance_cfg = {
        "agent": agent_ref,
        "name": name or os.path.basename(instance_dir),
    }

    # 自动生成路径覆盖
    abs_dir = os.path.abspath(instance_dir)
    instance_cfg["knowledge"] = {
        "baseDir": os.path.join(abs_dir, "knowledge"),
        "rawDir": os.path.join(abs_dir, "knowledge", "raw"),
        "processedDir": os.path.join(abs_dir, "knowledge", "processed"),
        "indexFile": os.path.join(abs_dir, "knowledge", "rag_index.json"),
        "keywordFile": os.path.join(abs_dir, "knowledge", "rag_index.keywords.json"),
    }
    instance_cfg["wiki"] = {
        "dir": os.path.join(abs_dir, "wiki"),
        "relationsFile": os.path.join(abs_dir, "wiki", "relations.json"),
    }

    if overrides:
        instance_cfg = _deep_merge(instance_cfg, overrides)

    instance_path = os.path.join(instance_dir, "instance.yml")
    with open(instance_path, "w", encoding="utf-8") as f:
        yaml.dump(instance_cfg, f, allow_unicode=True, default_flow_style=False)

    return instance_path


def _resolve_agent_dir(agent_ref: str, instance_dir: str) -> str:
    """
    解析 agent 引用为绝对目录路径。

    支持:
        "qaagent67wiki"                → 在 agentexample/ 下查找
        "../agentexample/qaagent67wiki" → 相对于 instance_dir 解析
        "/abs/path/to/agent"           → 绝对路径
    """
    # 绝对路径
    if os.path.isabs(agent_ref):
        return agent_ref

    # 相对于 instance_dir
    relative = os.path.join(instance_dir, agent_ref)
    if os.path.isdir(relative):
        return os.path.abspath(relative)

    # 在常见位置查找
    search_paths = [
        os.path.join(instance_dir, "..", "..", "agentexample", agent_ref),
        os.path.join(os.getcwd(), "agentexample", agent_ref),
        os.path.join(os.path.dirname(__file__), "..", "..", "agentexample", agent_ref),
    ]
    for path in search_paths:
        if os.path.isdir(path):
            return os.path.abspath(path)

    raise FileNotFoundError(
        f"Cannot resolve agent template '{agent_ref}'. "
        f"Searched: {instance_dir}, agentexample/"
    )
