from __future__ import annotations

import json
from pathlib import Path
from typing import TYPE_CHECKING, Any

from aicd.agent.tool_args_coerce import coerce_tool_json_array, coerce_tool_json_object
from aicd.tasks.safe_ids import validate_topic_slug
from aicd.tools.doc_project.kit import (
    doc_manifest_coverage_report,
    doc_manifest_validate,
    doc_project_export,
    doc_revision_diff_summary,
    doc_text_health_check,
    get_doc_project,
    init_doc_project,
    library_doc_batch_quality_pass,
    library_doc_node_resolve,
    library_doc_snapshot,
    library_resources_scan,
    manifest_path,
    patch_doc_project,
)

if TYPE_CHECKING:
    from aicd.agent.tool_router import ToolRouter


def _slug(router: ToolRouter, raw: Any) -> str | dict[str, Any]:
    try:
        return validate_topic_slug(str(raw).strip()) or ""
    except ValueError as e:
        return {"ok": False, "error": str(e)}


def _library_doc_project_init(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    title = str(args.get("title") or sid).strip() or sid
    return init_doc_project(
        router._ai_output,
        sid,
        title=title,
        policy_check=router._policy.check_allowed,
        overwrite_manifest=bool(args.get("overwrite_manifest", False)),
    )


def _library_doc_project_get(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    return get_doc_project(
        router._ai_output,
        sid,
        policy_check=router._policy.check_allowed,
    )


def _library_doc_project_patch(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    raw = args.get("updates")
    updates, err = coerce_tool_json_object(raw, "updates")
    if err:
        return {"ok": False, "error": err}
    return patch_doc_project(
        router._ai_output,
        sid,
        updates,
        policy_check=router._policy.check_allowed,
        replace_resources=bool(args.get("replace_resources", False)),
    )


def _library_doc_snapshot(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    sub = args.get("subpath")
    sub_s = str(sub).strip() if sub is not None and str(sub).strip() else None
    return library_doc_snapshot(
        router._ai_output,
        sid,
        policy_check=router._policy.check_allowed,
        subpath=sub_s,
    )


def _doc_manifest_validate_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    from aicd.tools.doc_project.kit import library_path

    lib = library_path(router._ai_output, sid)
    router._policy.check_allowed(lib)
    mp = manifest_path(lib)
    router._policy.check_allowed(mp)
    if not mp.is_file():
        return {"ok": False, "error": f"no doc_project.json for {sid!r}"}
    try:
        man = json.loads(mp.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        return {"ok": False, "error": str(e)}
    v = doc_manifest_validate(
        lib, man, check_paths_exist=bool(args.get("check_paths_exist", True))
    )
    return {"ok": True, "doc_slug": sid, **v}


def _doc_manifest_coverage_report_tool(
    router: ToolRouter, args: dict[str, Any]
) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    return doc_manifest_coverage_report(
        router._ai_output,
        sid,
        policy_check=router._policy.check_allowed,
        scan_markdown_dir=bool(args.get("scan_markdown_dir", True)),
    )


def _library_resources_scan_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    return library_resources_scan(
        router._ai_output,
        sid,
        policy_check=router._policy.check_allowed,
        include_snapshots=bool(args.get("include_snapshots", False)),
    )


def _doc_text_health_check_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    raw = args.get("paths")
    coerced = coerce_tool_json_array(raw, "paths", allow_empty=False)
    if isinstance(coerced, dict):
        return coerced
    paths: list[Path] = []
    for i, p in enumerate(coerced):
        if not isinstance(p, str) or not str(p).strip():
            return {"ok": False, "error": f"paths[{i}] must be non-empty string"}
        paths.append(router._resolve_path(str(p).strip()))
    return doc_text_health_check(paths, policy_check=router._policy.check_allowed)


def _doc_project_export_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    try:
        ts = validate_topic_slug(str(args.get("topic_slug") or sid).strip())
    except ValueError as e:
        return {"ok": False, "error": str(e)}
    if not ts:
        return {"ok": False, "error": "topic_slug required"}
    fmts = coerce_tool_json_array(args.get("formats"), "formats", allow_empty=False)
    if isinstance(fmts, dict):
        return fmts
    formats = [str(x).strip() for x in fmts if str(x).strip()]
    smr = str(args.get("source_markdown_rel") or "").strip()
    if not smr:
        return {"ok": False, "error": "source_markdown_rel required (path under library)"}
    pextra = args.get("pandoc_extra_args")
    pandoc_args: list[str] | None = None
    if isinstance(pextra, list):
        pandoc_args = [str(x) for x in pextra]
    return doc_project_export(
        router._ai_output,
        sid,
        topic_slug=ts,
        source_markdown_rel=smr,
        formats=formats,
        policy_check=router._policy.check_allowed,
        pandoc_extra_args=pandoc_args,
    )


def _doc_revision_diff_summary_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    before = router._resolve_path(str(args["path_before"]).strip())
    after = router._resolve_path(str(args["path_after"]).strip())
    return doc_revision_diff_summary(
        before, after, policy_check=router._policy.check_allowed
    )


def _library_doc_batch_quality_pass_tool(
    router: ToolRouter, args: dict[str, Any]
) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    return library_doc_batch_quality_pass(
        router._ai_output,
        sid,
        policy_check=router._policy.check_allowed,
        check_paths_exist=bool(args.get("check_paths_exist", True)),
        run_text_health=bool(args.get("run_text_health", True)),
        scan_resources=bool(args.get("scan_resources", True)),
    )


def _library_doc_node_resolve_tool(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    sid = _slug(router, args.get("doc_slug"))
    if isinstance(sid, dict):
        return sid
    if not sid:
        return {"ok": False, "error": "doc_slug required"}
    nid = str(args.get("node_id") or "").strip()
    if not nid:
        return {"ok": False, "error": "node_id required"}
    return library_doc_node_resolve(
        router._ai_output, sid, nid, policy_check=router._policy.check_allowed
    )


HANDLERS: dict[str, Any] = {
    "library_doc_project_init": _library_doc_project_init,
    "library_doc_project_get": _library_doc_project_get,
    "library_doc_project_patch": _library_doc_project_patch,
    "library_doc_snapshot": _library_doc_snapshot,
    "doc_manifest_validate": _doc_manifest_validate_tool,
    "doc_manifest_coverage_report": _doc_manifest_coverage_report_tool,
    "library_resources_scan": _library_resources_scan_tool,
    "doc_text_health_check": _doc_text_health_check_tool,
    "doc_project_export": _doc_project_export_tool,
    "doc_revision_diff_summary": _doc_revision_diff_summary_tool,
    "library_doc_batch_quality_pass": _library_doc_batch_quality_pass_tool,
    "library_doc_node_resolve": _library_doc_node_resolve_tool,
}
