"""
Quality A/B Test — Gemma 4 26B vs Qwen 3.5 35B
실제 서비스 시나리오 기반 품질 비교
"""
import urllib.request, json, time, sys, os

try:
    sys.stdout.reconfigure(encoding='utf-8')
except:
    pass

BASE = "http://127.0.0.1:8000"
MODEL_NAME = sys.argv[1] if len(sys.argv) > 1 else "unknown"
OUTPUT_FILE = f"scripts/quality_result_{MODEL_NAME}.json"

SCENARIOS = [
    # ═══ 1. 코딩 에이전트 (VS Code) ═══
    {
        "id": "code_generate",
        "category": "coding",
        "name": "Python 함수 생성",
        "prompt": "Write a Python function `merge_sorted_lists(list1, list2)` that merges two sorted lists into one sorted list without using built-in sort. Include type hints, docstring, and handle edge cases. Show only the code.",
        "eval_criteria": ["correctness", "type_hints", "docstring", "edge_cases"]
    },
    {
        "id": "code_debug",
        "category": "coding",
        "name": "버그 찾기 & 수정",
        "prompt": """Find and fix the bug in this code:
```python
def find_duplicates(arr):
    seen = {}
    duplicates = []
    for item in arr:
        if item in seen:
            duplicates.append(item)
        seen[item] = True
    return list(set(duplicates))

# Bug: find_duplicates([1,2,2,3,3,3]) returns [2,3] but
# find_duplicates([]) crashes with unexpected behavior
# Also it should return count of each duplicate
```
Fix it to return a dict like {2: 2, 3: 3} (value=count of occurrences).""",
        "eval_criteria": ["bug_identified", "correct_fix", "clean_code"]
    },
    {
        "id": "code_refactor",
        "category": "coding",
        "name": "TypeScript 리팩토링",
        "prompt": """Refactor this messy TypeScript into clean, typed code:
```typescript
async function getData(url, retry, timeout) {
  let result = null
  for (let i = 0; i < retry; i++) {
    try {
      const r = await fetch(url, {signal: AbortSignal.timeout(timeout)})
      if (r.ok) {
        result = await r.json()
        break
      }
    } catch(e) {
      if (i === retry - 1) throw e
      await new Promise(r => setTimeout(r, 1000 * (i+1)))
    }
  }
  return result
}
```
Add proper types, error handling, configurable backoff, and make it production-ready.""",
        "eval_criteria": ["types", "error_handling", "backoff", "production_quality"]
    },

    # ═══ 2. 개인 비서 (Discord Bot) — 한국어 ═══
    {
        "id": "korean_schedule",
        "category": "assistant_kr",
        "name": "한국어 일정 관리",
        "prompt": "내일 오후 2시에 팀 미팅이 있고, 3시에 치과 예약이 있어. 그리고 저녁 7시에 친구랑 홍대에서 만나기로 했어. 이 일정들을 정리해주고, 이동 시간을 고려해서 현실적으로 가능한지 알려줘. 서울 기준으로.",
        "eval_criteria": ["korean_fluency", "schedule_analysis", "practical_advice"]
    },
    {
        "id": "korean_email",
        "category": "assistant_kr",
        "name": "한국어 이메일 요약",
        "prompt": """다음 이메일을 3줄로 요약하고, 필요한 액션을 정리해줘:

안녕하세요 김팀장님,

지난 주 논의했던 Q2 마케팅 예산 관련하여 연락드립니다. 
본부장님께서 기존 제안 대비 15% 삭감을 요청하셨습니다. 
이에 따라 디지털 마케팅 채널 중 ROI가 낮은 채널을 우선 정리해야 할 것 같습니다.

리서치팀에서는 네이버 검색광고 대비 인스타그램 광고의 전환율이 
0.3%로 가장 낮다는 분석 결과를 공유했습니다. 
수요일까지 수정안을 제출해야 하니, 화요일 오전까지 
각 채널별 삭감 우선순위를 정리해서 회신 부탁드립니다.

감사합니다.
마케팅팀 박과장 드림""",
        "eval_criteria": ["korean_summary", "action_items", "conciseness"]
    },

    # ═══ 3. MCP 도구 (Function Calling) ═══
    {
        "id": "tool_calling",
        "category": "tool_use",
        "name": "Function Calling (JSON)",
        "prompt": """You have access to these tools:
- search_web(query: string) -> string
- get_calendar(date: string) -> list[Event]  
- send_email(to: string, subject: string, body: string) -> bool

User says: "Check my calendar for tomorrow, and if I have a meeting with John, search for the latest quarterly report and email him a summary."

Respond with the exact sequence of tool calls as JSON array. Use this format:
[{"tool": "name", "args": {...}}, ...]""",
        "eval_criteria": ["correct_sequence", "valid_json", "complete_args"]
    },
    {
        "id": "structured_output",
        "category": "tool_use",
        "name": "구조화 출력 (JSON)",
        "prompt": """Parse this unstructured text into a JSON object:

"삼성전자가 2026년 1분기 실적을 발표했다. 매출은 79조원으로 전년 동기 대비 12% 증가했고, 영업이익은 15.2조원을 기록했다. 반도체 부문이 전체 이익의 65%를 차지했으며, 특히 HBM4 수요 증가로 인해 메모리 사업부 매출이 전 분기 대비 23% 성장했다."

Output format:
{
  "company": "",
  "period": "",
  "revenue": {"amount": "", "unit": "", "yoy_change": ""},
  "operating_profit": {"amount": "", "unit": ""},
  "segments": [{"name": "", "profit_share": "", "highlights": ""}]
}""",
        "eval_criteria": ["correct_parsing", "valid_json", "completeness"]
    },

    # ═══ 4. 일반 추론 ═══
    {
        "id": "reasoning",
        "category": "reasoning",
        "name": "논리 추론",
        "prompt": "A farmer has 3 fields. Field A produces 20% more wheat than Field B. Field C produces 15% less than Field A. Together, all three fields produced 1,000 kg of wheat. How much did each field produce? Show your work step by step.",
        "eval_criteria": ["correct_answer", "clear_steps", "math_accuracy"]
    },
]


def ask(prompt, max_tokens=800):
    payload = json.dumps({
        "model": "m",
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": max_tokens,
        "temperature": 0
    }).encode()
    req = urllib.request.Request(
        f"{BASE}/v1/chat/completions",
        data=payload,
        headers={"Content-Type": "application/json"}
    )
    t0 = time.time()
    resp = json.loads(urllib.request.urlopen(req, timeout=120).read())
    dt = time.time() - t0
    usage = resp.get("usage", {})
    content = resp["choices"][0]["message"]["content"]
    return {
        "content": content,
        "tokens": usage.get("completion_tokens", 0),
        "time": round(dt, 2),
        "tps": round(usage.get("completion_tokens", 0) / dt, 2) if dt > 0 else 0
    }


def main():
    print(f"{'='*60}")
    print(f"  Quality A/B Test — Model: {MODEL_NAME}")
    print(f"  {len(SCENARIOS)} scenarios | {time.strftime('%Y-%m-%d %H:%M')}")
    print(f"{'='*60}\n")

    # Health check
    try:
        req = urllib.request.Request(f"{BASE}/health")
        resp = json.loads(urllib.request.urlopen(req, timeout=5).read())
        if resp.get("status") != "ok":
            print("Server not ready!")
            sys.exit(1)
    except Exception as e:
        print(f"Server not reachable: {e}")
        sys.exit(1)

    # Warmup
    print("Warmup...", flush=True)
    ask("Hello", max_tokens=10)
    print("Done\n", flush=True)

    results = []
    for i, sc in enumerate(SCENARIOS):
        print(f"[{i+1}/{len(SCENARIOS)}] {sc['category']} — {sc['name']}")
        print(f"  Prompt: {sc['prompt'][:80]}...", flush=True)

        try:
            resp = ask(sc["prompt"])
            print(f"  ✅ {resp['tokens']} tokens | {resp['tps']:.1f} t/s | {resp['time']}s")
            print(f"  Response preview: {resp['content'][:120]}...\n")

            results.append({
                "id": sc["id"],
                "category": sc["category"],
                "name": sc["name"],
                "model": MODEL_NAME,
                "response": resp["content"],
                "tokens": resp["tokens"],
                "time": resp["time"],
                "tps": resp["tps"],
                "eval_criteria": sc["eval_criteria"]
            })
        except Exception as e:
            print(f"  ❌ Error: {e}\n")
            results.append({
                "id": sc["id"],
                "category": sc["category"],
                "name": sc["name"],
                "model": MODEL_NAME,
                "response": f"ERROR: {e}",
                "tokens": 0,
                "time": 0,
                "tps": 0,
            })

    # Save
    with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)
    print(f"\n{'='*60}")
    print(f"  Results saved: {OUTPUT_FILE}")
    print(f"  Total scenarios: {len(results)}")
    print(f"{'='*60}")


if __name__ == "__main__":
    main()
