from ai_testing_swarm.agents.test_planner_agent import TestPlannerAgent
from ai_testing_swarm.agents.execution_agent import ExecutionAgent
from ai_testing_swarm.agents.llm_reasoning_agent import LLMReasoningAgent
from ai_testing_swarm.agents.learning_agent import LearningAgent
from ai_testing_swarm.agents.release_gate_agent import ReleaseGateAgent
from ai_testing_swarm.reporting.report_writer import write_report
from ai_testing_swarm.core.safety import enforce_public_only

import logging

logger = logging.getLogger(__name__)
EXPECTED_FAILURES = {
    "success",
    "missing_param",
    "invalid_param",
    "security",
    "method_not_allowed",
    "not_found",
    "content_negotiation",
}

class SwarmOrchestrator:
    """
    Central brain of the AI Testing Swarm.
    """

    def __init__(self):
        self.planner = TestPlannerAgent()
        self.executor = ExecutionAgent()
        self.reasoner = LLMReasoningAgent()
        self.learner = LearningAgent()
        self.release_gate = ReleaseGateAgent()

    def run(self, request: dict):
        """Runs the full AI testing swarm and returns (decision, results)."""

        # Safety hook (currently no-op; kept for backward compatibility)
        enforce_public_only(request["url"])
        # --------------------------------------------------------
        # 1️⃣ PLAN TESTS
        # --------------------------------------------------------
        tests = self.planner.plan(request)
        results = []

        logger.info("🧠 Planned %d tests", len(tests))
        print(f"🧠 Planned {len(tests)} tests")

        # --------------------------------------------------------
        # 2️⃣ EXECUTE + CLASSIFY (parallel)
        # --------------------------------------------------------
        from concurrent.futures import ThreadPoolExecutor, as_completed
        from ai_testing_swarm.core.config import AI_SWARM_WORKERS, AI_SWARM_MAX_TESTS

        # Cap test count (avoid DoS on target / CI timeouts)
        tests = tests[:AI_SWARM_MAX_TESTS]

        def run_one(test: dict):
            test_name = test.get("name")
            execution_result = self.executor.execute(request, test)
            execution_result["name"] = test_name
            classification = self.reasoner.reason(execution_result)
            execution_result.update({
                "reason": classification.get("type"),
                "confidence": classification.get("confidence", 1.0),
                "failure_type": classification.get("type"),
                "status": (
                    "PASSED" if classification.get("type") in EXPECTED_FAILURES else "FAILED"
                ),
            })
            # Optional learning step
            try:
                self.learner.learn(test_name, classification)
            except Exception as e:
                logger.warning("⚠️ Learning skipped for %s: %s", test_name, e)
            return execution_result

        results_by_name = {}
        with ThreadPoolExecutor(max_workers=AI_SWARM_WORKERS) as ex:
            futs = [ex.submit(run_one, t) for t in tests]
            for fut in as_completed(futs):
                r = fut.result()
                results_by_name[r["name"]] = r

        # Preserve deterministic order
        for t in tests:
            results.append(results_by_name[t["name"]])

        # --------------------------------------------------------
        # 3️⃣ RELEASE DECISION
        # --------------------------------------------------------
        decision = self.release_gate.decide(results)

        # --------------------------------------------------------
        # 4️⃣ WRITE JSON REPORT
        # --------------------------------------------------------
        meta = {"decision": decision}

        # Optional AI summary for humans (best-effort)
        try:
            from ai_testing_swarm.core.config import AI_SWARM_AI_SUMMARY, AI_SWARM_OPENAI_MODEL, AI_SWARM_USE_OPENAI

            if AI_SWARM_USE_OPENAI and AI_SWARM_AI_SUMMARY:
                from ai_testing_swarm.core.openai_client import summarize_run

                meta["ai_summary"] = summarize_run(
                    request=request,
                    decision=decision,
                    results=results,
                    model=AI_SWARM_OPENAI_MODEL,
                )
        except Exception as e:
            meta["ai_summary_error"] = str(e)

        report_path = write_report(request, results, meta=meta)
        logger.info("📄 Swarm JSON report written to: %s", report_path)
        print(f"📄 Swarm JSON report written to: {report_path}")

        return decision, results
