"""
Lab 5.10: CI/CD Pipeline -- SOLUTION

Golden dataset evaluation + automated regression gate.
"""

import sys

GOLDEN_DATASET_EXAMPLE = [
    {"id": "t001", "input": "Find users created in last 24h",
     "expected_tools": ["query_database"],
     "expected_params": {"query": "SELECT * FROM users WHERE created_at > now() - interval '24 hours'"},
     "expected_output_contains": ["users"], "weight": 1.0},
    {"id": "t002", "input": "What's the weather in Tokyo?",
     "expected_tools": ["search_web", "fetch_weather"],
     "expected_output_contains": ["Tokyo", "°"], "weight": 1.0},
    {"id": "t003", "input": "Deploy the latest build to staging",
     "expected_tools": ["run_deploy"],
     "expected_params": {"environment": "staging"},
     "expected_output_contains": ["deploy", "staging"], "weight": 1.5},
    {"id": "t004", "input": "Explain how recursion works",
     "expected_tools": [],
     "expected_output_contains": ["base case", "recursive"], "weight": 0.5},
    {"id": "t005", "input": "Find and fix the security vulnerability in auth.py",
     "expected_tools": ["read_file", "edit_file"],
     "expected_output_contains": ["fixed", "vulnerability"], "weight": 2.0},
]


def default_agent(input_text: str) -> dict:
    """Reference agent for testing. Replace with your actual agent."""
    return {
        "tool_calls": [{"tool": "query_database", "params": {"query": "SELECT * FROM users"}}],
        "output": "Found users in database"
    }


class AgentCIPipeline:
    def __init__(self, golden_dataset: list[dict], threshold: float = 0.8):
        self.dataset = golden_dataset
        self.threshold = threshold
    
    def score_case(self, agent_result: dict, test_case: dict) -> float:
        score = 0.0
        max_score = 0.0
        
        # Tool selection (40% of score)
        expected_tools = set(test_case.get("expected_tools", []))
        actual_tools = set(t.get("tool") for t in agent_result.get("tool_calls", []))
        if expected_tools:
            overlap = expected_tools & actual_tools
            score += len(overlap) / len(expected_tools) * 0.4
            max_score += 0.4
        else:
            score += 0.4  # No tools expected, passed
            max_score += 0.4
        
        # Tool params (30% of score)
        expected_params = test_case.get("expected_params", {})
        if expected_params:
            actual_tools_list = agent_result.get("tool_calls", [])
            for at in actual_tools_list:
                for key, val in expected_params.items():
                    if at.get("params", {}).get(key) == val:
                        score += 0.3 / len(expected_params)
            max_score += 0.3
        else:
            score += 0.3
            max_score += 0.3
        
        # Output content (30% of score)
        output = agent_result.get("output", "")
        expected_contains = test_case.get("expected_output_contains", [])
        if expected_contains:
            matches = sum(1 for s in expected_contains if s.lower() in output.lower())
            score += (matches / len(expected_contains)) * 0.3
            max_score += 0.3
        else:
            score += 0.3
            max_score += 0.3
        
        return score / max_score if max_score > 0 else 0.0
    
    def run_eval(self, agent_func=default_agent, k: int = 3) -> dict:
        results = []
        for case in self.dataset:
            case_scores = []
            for attempt in range(k):
                result = agent_func(case["input"])
                case_scores.append(self.score_case(result, case))
            
            best_score = max(case_scores)
            passed = best_score >= 0.7
            
            results.append({
                "id": case["id"],
                "input": case["input"][:50],
                "best_score": round(best_score, 3),
                "passed": passed,
                "weight": case.get("weight", 1.0)
            })
        
        weighted_pass = sum(r["weight"] for r in results if r["passed"])
        total_weight = sum(r["weight"] for r in results)
        pass_rate = weighted_pass / total_weight if total_weight > 0 else 0
        
        return {
            "pass@1": pass_rate,
            "pass@k": pass_rate,
            "k": k,
            "results": results,
            "total_cases": len(results),
            "passed_cases": sum(1 for r in results if r["passed"])
        }
    
    def check_gate(self, eval_results: dict) -> bool:
        return eval_results["pass@k"] >= self.threshold
    
    def promote(self):
        print("  -> Copying config to production...")
        print("  -> Restarting agent service...")
        print("  -> Deploying new prompt version...")
        print("  + Production deployment complete")
    
    def rollback(self, reason: str):
        print(f"  -> Rolling back: {reason}")
        print("  -> Restoring previous agent config...")
        print("  -> Pinning model to previous version...")
        print("  + Rollback complete")
        print("  -> Alert sent to #agent-alerts")
    
    def run(self, agent_func=default_agent):
        print("=" * 55)
        print(" AGENT CI/CD PIPELINE")
        print("=" * 55)
        
        print("\n [CHECKOUT] Loading agent config from agent-config-v42.yaml")
        print("   Config hash: a1b2c3d4")
        print("   Model: claude-sonnet-4-20260501")
        
        print("\n [EVAL] Running golden dataset...")
        results = self.run_eval(agent_func, k=3)
        
        print(f"   pass@1: {results['pass@1']:.1%}")
        print(f"   pass@k: {results['pass@k']:.1%}")
        print(f"   Passed: {results['passed_cases']}/{results['total_cases']} cases")
        
        print("\n   Case breakdown:")
        for r in results["results"]:
            icon = "+" if r["passed"] else "-"
            print(f"   {icon} {r['id']}: score={r['best_score']:.1%} (weight={r['weight']})")
        
        print(f"\n [GATE] Threshold: {self.threshold:.0%}")
        if self.check_gate(results):
            print(f"   + GATE PASSED (pass@k={results['pass@k']:.1%} >= {self.threshold:.0%})")
            print("\n [PROMOTE] Promoting to production...")
            self.promote()
            print("\n [OK] Pipeline complete")
        else:
            print(f"   - GATE FAILED (pass@k={results['pass@k']:.1%} < {self.threshold:.0%})")
            print("\n [ROLLBACK] Reverting...")
            self.rollback(f"pass@k {results['pass@k']:.1%} below threshold")
            print("\n [FAIL] Pipeline failed")
            sys.exit(1)


if __name__ == "__main__":
    pipeline = AgentCIPipeline(GOLDEN_DATASET_EXAMPLE, threshold=0.8)
    pipeline.run()
