"""
Engineering Graph — 主工程工作流
prepare_sandbox → analyze_project → architect → developer → tester → reviewer → reporter → git_manager
"""
from typing import Literal
import structlog
from langgraph.graph import StateGraph, END

from app.state import EngineeringState
from app.agents import (
    SupervisorAgent, ArchitectAgent, DeveloperAgent,
    TesterAgent, ReviewerAgent, ReporterAgent,
)
from app.tools.sandbox import SandboxManager
from app.tools.git_manager import GitManager
from app.configs import settings

logger = structlog.get_logger()

# ─── Node functions ──────────────────────────────────────────────────────────

def node_prepare_sandbox(state: EngineeringState) -> EngineeringState:
    logger.info("Node: prepare_sandbox")
    manager = SandboxManager()
    sandbox_path = manager.create_sandbox(state["task_id"], state["repo_path"])
    return {**state, "sandbox_path": sandbox_path, "status": "sandbox_ready"}


def node_analyze_project(state: EngineeringState) -> EngineeringState:
    logger.info("Node: analyze_project")
    supervisor = SupervisorAgent()
    analysis = supervisor.analyze(state)
    return {
        **state,
        "project_analysis": analysis,
        "implementation_plan": analysis.get("tasks", []),
        "status": "analyzed",
    }


def node_architect(state: EngineeringState) -> EngineeringState:
    logger.info("Node: architect")
    architect = ArchitectAgent()
    plan = architect.design(state)
    return {**state, "architecture_plan": plan, "status": "architected"}


def node_developer(state: EngineeringState) -> EngineeringState:
    logger.info("Node: developer", attempts=state.get("attempts", 0))
    developer = DeveloperAgent()
    result = developer.develop(state)

    code_result = {
        "output": result.output,
        "error": result.error,
        "success": result.success,
        "files_changed": result.files_changed,
        "exit_code": result.exit_code,
    }

    # compute diff
    diff_result = {}
    if state.get("sandbox_path") and state.get("repo_path"):
        try:
            manager = SandboxManager()
            diff_result = manager.get_diff(state["sandbox_path"], state["repo_path"])
        except Exception as e:
            logger.warning("Diff generation failed", error=str(e))

    status = "developed" if result.success else "failed"
    return {
        **state,
        "code_result": code_result,
        "diff_result": diff_result,
        "status": status,
        "attempts": state.get("attempts", 0) + 1,
    }


def node_tester(state: EngineeringState) -> EngineeringState:
    logger.info("Node: tester")
    tester = TesterAgent()
    test_result = tester.run_tests(state)
    status = "tested" if test_result.get("success") else "test_failed"
    return {**state, "test_result": test_result, "status": status}


def node_reviewer(state: EngineeringState) -> EngineeringState:
    logger.info("Node: reviewer")
    reviewer = ReviewerAgent()
    review_result = reviewer.review(state)
    status = "reviewed"
    return {**state, "review_result": review_result, "status": status}


def node_reporter(state: EngineeringState) -> EngineeringState:
    logger.info("Node: reporter")
    reporter = ReporterAgent(reports_dir="./reports")
    output = reporter.generate_report(state)
    return {
        **state,
        "report": output["report"],
        "pr_description": output["pr_description"],
        "status": "reported",
    }


def node_git_manager(state: EngineeringState) -> EngineeringState:
    logger.info("Node: git_manager")
    sandbox_path = state.get("sandbox_path", "")
    if not sandbox_path:
        return {**state, "status": "completed"}

    try:
        git = GitManager(sandbox_path)
        git.init()
        git.git_add()

        commit_msg = f"feat: {state['user_request'][:72]}\n\nTask ID: {state['task_id']}"
        git.git_commit(commit_msg)

        diff_stat = git.git_diff_stat()
        changed = git.changed_files()

        diff_result = {**state.get("diff_result", {}), "git_stat": diff_stat, "changed_files": changed}
        return {**state, "diff_result": diff_result, "status": "completed"}
    except Exception as e:
        logger.warning("Git manager failed", error=str(e))
        return {**state, "status": "completed"}


# ─── Routing functions ────────────────────────────────────────────────────────

def route_after_developer(state: EngineeringState) -> Literal["tester", "developer", "reporter"]:
    """开发后：成功 → 测试；失败但可重试 → 重新开发；失败超限 → 报告"""
    if state["status"] == "developed":
        return "tester"
    if state.get("attempts", 0) < state.get("max_attempts", 3):
        logger.info("Development failed, retrying", attempts=state.get("attempts"))
        return "developer"
    logger.warning("Max attempts reached, going to reporter")
    return "reporter"


def route_after_tester(state: EngineeringState) -> Literal["reviewer", "developer"]:
    """测试后：通过 → 审查；失败且可重试 → 重新开发"""
    if state["status"] == "tested":
        return "reviewer"
    if state.get("attempts", 0) < state.get("max_attempts", 3):
        return "developer"
    return "reviewer"


def route_after_reviewer(state: EngineeringState) -> Literal["reporter", "developer"]:
    """审查后：通过 → 报告；有严重问题且可重试 → 修复"""
    reviewer = ReviewerAgent()
    if reviewer.needs_fix(state.get("review_result", {})) and state.get("attempts", 0) < state.get("max_attempts", 3):
        return "developer"
    return "reporter"


# ─── Graph builder ────────────────────────────────────────────────────────────

def build_engineering_graph() -> StateGraph:
    graph = StateGraph(EngineeringState)

    graph.add_node("prepare_sandbox", node_prepare_sandbox)
    graph.add_node("analyze_project", node_analyze_project)
    graph.add_node("architect", node_architect)
    graph.add_node("developer", node_developer)
    graph.add_node("tester", node_tester)
    graph.add_node("reviewer", node_reviewer)
    graph.add_node("reporter", node_reporter)
    graph.add_node("git_manager", node_git_manager)

    graph.set_entry_point("prepare_sandbox")
    graph.add_edge("prepare_sandbox", "analyze_project")
    graph.add_edge("analyze_project", "architect")
    graph.add_edge("architect", "developer")

    graph.add_conditional_edges("developer", route_after_developer, {
        "tester": "tester",
        "developer": "developer",
        "reporter": "reporter",
    })
    graph.add_conditional_edges("tester", route_after_tester, {
        "reviewer": "reviewer",
        "developer": "developer",
    })
    graph.add_conditional_edges("reviewer", route_after_reviewer, {
        "reporter": "reporter",
        "developer": "developer",
    })
    graph.add_edge("reporter", "git_manager")
    graph.add_edge("git_manager", END)

    return graph.compile()


engineering_graph = build_engineering_graph()
