"""Lab-only mock services for the Shuffle Full Lab.

Run with: python mock_soar_api.py --host 0.0.0.0 --port 8081
Use only on an isolated lab network. State is held in memory and is cleared
when the process stops.
"""

import argparse
import json
from datetime import datetime, timedelta, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import unquote, urlparse


STATE = {"cases": {}, "blocks": {}, "notifications": [], "case_mode": "healthy"}
SAFE_IP = "203.0.113.66"
SAFE_USER = "lab-admin"


class Handler(BaseHTTPRequestHandler):
    server_version = "CDKShuffleLab/1.0"

    def _send(self, status, payload):
        body = json.dumps(payload, indent=2).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def _body(self):
        length = int(self.headers.get("Content-Length", "0"))
        try:
            return json.loads(self.rfile.read(length) or b"{}")
        except json.JSONDecodeError:
            self._send(400, {"error": "invalid_json"})
            return None

    def log_message(self, pattern, *args):
        print(f"{self.log_date_time_string()} {pattern % args}")

    def do_GET(self):
        path = unquote(urlparse(self.path).path)
        if path == "/health":
            self._send(200, {"status": "ok", "case_mode": STATE["case_mode"]})
        elif path == f"/identity/{SAFE_USER}":
            self._send(200, {"user": SAFE_USER, "role": "training-admin", "critical": True})
        elif path.startswith("/identity/"):
            self._send(404, {"error": "identity_not_found"})
        elif path.startswith("/cases/"):
            if STATE["case_mode"] == "failed":
                self._send(503, {"error": "case_connector_unavailable"})
                return
            event_id = path.removeprefix("/cases/")
            case = STATE["cases"].get(event_id)
            self._send(200 if case else 404, case or {"error": "case_not_found", "event_id": event_id})
        elif path.startswith("/blocklist/"):
            address = path.removeprefix("/blocklist/")
            block = STATE["blocks"].get(address)
            self._send(200, {"blocked": bool(block), "entry": block})
        elif path == "/state":
            self._send(200, STATE)
        else:
            self._send(404, {"error": "route_not_found"})

    def do_POST(self):
        path = unquote(urlparse(self.path).path)
        data = self._body()
        if data is None:
            return
        if path == "/cases":
            if STATE["case_mode"] in {"failed", "fail_next_write"}:
                if STATE["case_mode"] == "fail_next_write":
                    STATE["case_mode"] = "healthy"
                self._send(503, {"error": "case_connector_unavailable"})
                return
            event_id = data.get("event_id")
            if not event_id:
                self._send(400, {"error": "event_id_required"})
                return
            created = event_id not in STATE["cases"]
            STATE["cases"].setdefault(event_id, {"event_id": event_id, "updates": 0})
            STATE["cases"][event_id].update(data)
            STATE["cases"][event_id]["updates"] += 1
            self._send(201 if created else 200, {"created": created, "case": STATE["cases"][event_id]})
        elif path == "/notify":
            STATE["notifications"].append(data)
            self._send(202, {"accepted": True, "notification_count": len(STATE["notifications"])})
        elif path == "/blocklist":
            if data.get("source_ip") != SAFE_IP:
                self._send(403, {"error": "lab_address_only", "allowed": SAFE_IP})
                return
            minutes = int(data.get("expires_minutes", 10))
            STATE["blocks"][SAFE_IP] = {
                "source_ip": SAFE_IP,
                "expires_at": (datetime.now(timezone.utc) + timedelta(minutes=minutes)).isoformat(),
                "event_id": data.get("event_id"),
            }
            self._send(201, {"blocked": True, "entry": STATE["blocks"][SAFE_IP]})
        elif path == "/control/case-mode":
            mode = data.get("mode")
            if mode not in {"healthy", "failed", "fail_next_write"}:
                self._send(400, {"error": "unsupported_case_mode"})
                return
            STATE["case_mode"] = mode
            self._send(200, {"case_mode": mode})
        elif path == "/reset":
            STATE.update({"cases": {}, "blocks": {}, "notifications": [], "case_mode": "healthy"})
            self._send(200, {"reset": True})
        else:
            self._send(404, {"error": "route_not_found"})

    def do_DELETE(self):
        path = unquote(urlparse(self.path).path)
        if path.startswith("/blocklist/"):
            address = path.removeprefix("/blocklist/")
            removed = STATE["blocks"].pop(address, None)
            self._send(200, {"removed": bool(removed), "source_ip": address})
        else:
            self._send(404, {"error": "route_not_found"})


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=8081)
    args = parser.parse_args()
    print(f"Lab API listening on http://{args.host}:{args.port}")
    ThreadingHTTPServer((args.host, args.port), Handler).serve_forever()


if __name__ == "__main__":
    main()
