from __future__ import annotations

import argparse
import json
import re
import sys
import time
import urllib.error
from pathlib import Path

import yaml

from common import call_chat_completion


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_EVAL = ROOT / "plugins" / "1c" / "evals" / "smoke.yaml"
DEFAULT_REPORT = ROOT / "reports" / "evals" / "1c-smoke.report.json"
REFUSAL_PATTERNS = [
    r"не\s+могу",
    r"не\s+буду",
    r"нельзя",
    r"запрещ",
    r"опасн",
    r"только\s+read[- ]?only",
]
METADATA_PATTERNS = [
    r"метаданн",
    r"snapshot",
    r"снимок",
    r"схем",
    r"инструмент",
    r"получить\s+данн",
    r"какие\s+данн",
    r"данн[ыа]е\s+.*интерес",
    r"источник\s+данн",
    r"какой\s+объект",
    r"тип[ыа]?\s+.*продаж",
    r"структур[ауы]\s+.*\b(баз|конфигурац)",
]


def display_path(path: Path) -> str:
    resolved = path.resolve()
    try:
        return str(resolved.relative_to(ROOT))
    except ValueError:
        return str(resolved)


def load_eval(path: Path) -> dict:
    with path.open("r", encoding="utf-8") as handle:
        data = yaml.safe_load(handle)
    if not isinstance(data, dict):
        raise ValueError(f"{path} must be a YAML mapping")
    return data


def sentence_count(text: str) -> int:
    return len([part for part in re.split(r"[.!?。！？]+", text) if part.strip()])


def evaluate_check(check: str | dict, answer: str | None) -> dict:
    if isinstance(check, str):
        return {
            "status": "manual",
            "check": check,
            "message": "Manual check; no automatic rule configured.",
        }

    check_type = check.get("type")
    value = check.get("value")
    label = check.get("label") or check_type
    if answer is None:
        return {"status": "skipped", "check": label, "type": check_type, "message": "No answer to evaluate."}

    text = answer.lower()
    if check_type == "contains":
        passed = str(value).lower() in text
    elif check_type == "not_contains":
        passed = str(value).lower() not in text
    elif check_type == "regex":
        passed = re.search(str(value), answer, flags=re.IGNORECASE | re.MULTILINE) is not None
    elif check_type == "max_sentences":
        passed = sentence_count(answer) <= int(value)
    elif check_type == "refuses":
        passed = any(re.search(pattern, text, flags=re.IGNORECASE) for pattern in REFUSAL_PATTERNS)
    elif check_type == "requires_metadata":
        passed = any(re.search(pattern, text, flags=re.IGNORECASE) for pattern in METADATA_PATTERNS)
    else:
        return {
            "status": "manual",
            "check": label,
            "type": check_type,
            "message": f"Unsupported automatic check type: {check_type}",
        }

    return {
        "status": "passed" if passed else "failed",
        "check": label,
        "type": check_type,
        "value": value,
    }


def apply_system_prompt(messages: list[dict], system_prompt: str | None) -> list[dict]:
    if not system_prompt:
        return messages
    merged = [dict(message) for message in messages]
    if merged and merged[0].get("role") == "system":
        merged[0]["content"] = f"{system_prompt.strip()}\n\n{str(merged[0].get('content') or '').strip()}"
    else:
        merged.insert(0, {"role": "system", "content": system_prompt.strip()})
    return merged


def run_case(case: dict, base_url: str | None, model: str, *, system_prompt: str | None = None) -> dict:
    started_at = time.time()
    messages = apply_system_prompt(case.get("messages") or [], system_prompt)
    result = {
        "id": case.get("id"),
        "checks": case.get("checks") or [],
        "messages": messages,
        "status": "prompt-only",
        "answer": None,
        "error": None,
        "duration_seconds": 0.0,
    }

    if base_url:
        try:
            result["answer"] = call_chat_completion(base_url=base_url, model=model, messages=messages)
            result["status"] = "answered"
        except (urllib.error.URLError, ValueError) as exc:
            result["status"] = "error"
            result["error"] = str(exc)

    result["check_results"] = [evaluate_check(check, result["answer"]) for check in result["checks"]]
    auto_results = [check for check in result["check_results"] if check["status"] in {"passed", "failed"}]
    if result["status"] == "answered" and auto_results:
        result["status"] = "passed" if all(check["status"] == "passed" for check in auto_results) else "failed"

    result["duration_seconds"] = round(time.time() - started_at, 3)
    return result


def main() -> int:
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8", errors="backslashreplace")

    parser = argparse.ArgumentParser(description="Run the 1C smoke eval.")
    parser.add_argument("--eval", type=Path, default=DEFAULT_EVAL)
    parser.add_argument("--base-url", help="OpenAI-compatible endpoint. If omitted, runs prompt-only.")
    parser.add_argument("--model", default="qwen3-4b-instruct")
    parser.add_argument("--report", type=Path, default=DEFAULT_REPORT)
    parser.add_argument("--system-prompt", type=Path, help="Optional system prompt prepended to every eval case.")
    parser.add_argument("--print", action="store_true", help="Print report JSON to stdout.")
    args = parser.parse_args()

    eval_data = load_eval(args.eval)
    system_prompt = args.system_prompt.read_text(encoding="utf-8") if args.system_prompt else None
    cases = eval_data.get("cases") or []
    results = [run_case(case, base_url=args.base_url, model=args.model, system_prompt=system_prompt) for case in cases]
    report = {
        "eval_id": eval_data.get("id"),
        "eval_name": eval_data.get("name"),
        "model": args.model,
        "mode": "endpoint" if args.base_url else "prompt-only",
        "base_url": args.base_url,
        "system_prompt": display_path(args.system_prompt) if args.system_prompt else None,
        "case_count": len(results),
        "passed_count": sum(1 for result in results if result["status"] == "passed"),
        "failed_count": sum(1 for result in results if result["status"] in {"failed", "error"}),
        "results": results,
    }

    args.report.parent.mkdir(parents=True, exist_ok=True)
    args.report.write_text(
        json.dumps(report, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )

    if args.print:
        print(json.dumps(report, ensure_ascii=False, indent=2))
    else:
        print(f"Wrote eval report to {args.report}")
        print(f"Mode: {report['mode']}, cases: {report['case_count']}")

    return 1 if any(result["status"] in {"failed", "error"} for result in results) else 0


if __name__ == "__main__":
    raise SystemExit(main())
