"""HAD Digital MVP - Main HTTP Server.

A complete Hospitalisation a Domicile (HAD) digital platform for managing
chemotherapy toxicity monitoring in home care settings.

Usage:
    python app.py [--host HOST] [--port PORT] [--debug]
"""

import http.server
import json
import os
import secrets
import sys
import uuid
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlparse, parse_qs

sys.path.insert(0, str(Path(__file__).resolve().parent))

from config_manager import config, get_bundle_dir
from database import get_db
from user_store import authenticate, get_user, list_users, list_users_by_role
from ctcae_engine import get_ctcae_engine
from alert_engine import get_alert_engine
from audit_logger import log_action, get_audit_log
from seed_demo import seed_demo_data
from adapters.stub import StubAdapter
from adapters.glm import GLMAdapter

SESSIONS = {}
AI_ADAPTERS = {"stub": StubAdapter, "glm": GLMAdapter}


class HADRequestHandler(http.server.BaseHTTPRequestHandler):
    server_version = "HAD-Digital/1.0"

    def do_GET(self):
        parsed = urlparse(self.path)
        path = parsed.path.rstrip("/") or "/"
        params = parse_qs(parsed.query)

        if path == "/" or path == "/index.html":
            return self._serve_static("index.html")
        if path.startswith("/static/"):
            return self._serve_static(path[8:])

        routes = {
            "/api/whoami": lambda: self._api_whoami(),
            "/api/patients": lambda: self._api_list_patients(params),
            "/api/reports": lambda: self._api_list_reports(params),
            "/api/grades": lambda: self._api_list_grades(params),
            "/api/alerts": lambda: self._api_list_alerts(params),
            "/api/timeline": lambda: self._api_get_timeline(params),
            "/api/treatment-plan": lambda: self._api_get_treatment_plan(params),
            "/api/messages": lambda: self._api_list_messages(params),
            "/api/export/summary": lambda: self._api_export_summary(params),
            "/api/audit-log": lambda: self._api_audit_log(params),
            "/api/symptoms": lambda: self._api_list_symptoms(),
            "/api/guidance": lambda: self._api_get_guidance(params),
        }

        if path in routes:
            return routes[path]()
        if path.startswith("/api/patients/"):
            return self._api_get_patient(path.split("/")[3])

        self._json_response({"error": "Not found"}, 404)

    def do_POST(self):
        parsed = urlparse(self.path)
        path = parsed.path.rstrip("/")

        routes = {
            "/api/login": lambda: self._api_login(),
            "/api/logout": lambda: self._api_logout(),
            "/api/reports": lambda: self._api_submit_report(),
            "/api/messages": lambda: self._api_send_message(),
            "/api/chat": lambda: self._api_chat(),
        }

        if path in routes:
            return routes[path]()
        if path.startswith("/api/alerts/") and path.endswith("/acknowledge"):
            return self._api_acknowledge_alert(path.split("/")[3])

        self._json_response({"error": "Not found"}, 404)

    # Security headers
    def _add_security_headers(self):
        security = config.get("security", default={})
        self.send_header("Content-Security-Policy", security.get("csp", "default-src 'self'"))
        self.send_header("X-Content-Type-Options", security.get("x_content_type_options", "nosniff"))
        self.send_header("X-Frame-Options", security.get("x_frame_options", "DENY"))
        self.send_header("Referrer-Policy", security.get("referrer_policy", "strict-origin-when-cross-origin"))
        self.send_header("Cache-Control", "no-store, no-cache, must-revalidate")

    # Session management
    def _get_session(self):
        cookie_header = self.headers.get("Cookie", "")
        session_cookie = config.get("session", "cookie_name", default="HAD_SESSION")
        for cookie in cookie_header.split(";"):
            cookie = cookie.strip()
            if cookie.startswith(f"{session_cookie}="):
                sid = cookie.split("=", 1)[1]
                return SESSIONS.get(sid)
        return None

    def _set_session(self, user):
        sid = str(uuid.uuid4())
        SESSIONS[sid] = {"user": user, "created_at": datetime.now(timezone.utc).isoformat()}
        self._pending_cookies = getattr(self, "_pending_cookies", [])
        session_cookie = config.get("session", "cookie_name", default="HAD_SESSION")
        max_age = config.get("session", "max_age_seconds", default=86400)
        self._pending_cookies.append(f"{session_cookie}={sid}; Path=/; HttpOnly; SameSite=Strict; Max-Age={max_age}")

    def _clear_session(self):
        cookie_header = self.headers.get("Cookie", "")
        session_cookie = config.get("session", "cookie_name", default="HAD_SESSION")
        for cookie in cookie_header.split(";"):
            cookie = cookie.strip()
            if cookie.startswith(f"{session_cookie}="):
                sid = cookie.split("=", 1)[1]
                SESSIONS.pop(sid, None)
        self._pending_cookies = getattr(self, "_pending_cookies", [])
        self._pending_cookies.append(f"{session_cookie}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0")

    def _get_current_user(self):
        session = self._get_session()
        return session.get("user") if session else None

    # JSON helpers
    def _json_response(self, data, status=200):
        body = json.dumps(data, ensure_ascii=False, default=str).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self._add_security_headers()
        for cookie in getattr(self, "_pending_cookies", []):
            self.send_header("Set-Cookie", cookie)
        self._pending_cookies = []
        self.end_headers()
        self.wfile.write(body)

    def _read_json_body(self):
        cl = int(self.headers.get("Content-Length", 0))
        if cl == 0:
            return {}
        try:
            return json.loads(self.rfile.read(cl).decode("utf-8"))
        except json.JSONDecodeError:
            return {}

    # Static files
    def _serve_static(self, file_path):
        static_dir = get_bundle_dir() / "static"
        try:
            resolved = (static_dir / file_path).resolve()
            if not str(resolved).startswith(str(static_dir.resolve())):
                self._json_response({"error": "Path traversal blocked"}, 403)
                return
        except (ValueError, OSError):
            self._json_response({"error": "Invalid path"}, 400)
            return
        if not resolved.exists() or not resolved.is_file():
            self._json_response({"error": "File not found"}, 404)
            return
        ct = {".html": "text/html; charset=utf-8", ".css": "text/css; charset=utf-8",
              ".js": "application/javascript; charset=utf-8", ".json": "application/json; charset=utf-8",
              ".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml", ".ico": "image/x-icon"}
        try:
            content = resolved.read_bytes()
            self.send_response(200)
            self.send_header("Content-Type", ct.get(resolved.suffix.lower(), "application/octet-stream"))
            self.send_header("Content-Length", str(len(content)))
            self._add_security_headers()
            self.end_headers()
            self.wfile.write(content)
        except OSError:
            self._json_response({"error": "File read error"}, 500)

    # Auth API
    def _api_login(self):
        data = self._read_json_body()
        username = data.get("username", "").strip()
        password = data.get("password", "")
        if not username or not password:
            self._json_response({"error": "Username and password required"}, 400)
            return
        ip = self.client_address[0] if self.client_address else None
        user = authenticate(username, password, ip)
        if user is None:
            log_action(action="login_failed", details=f"Failed login for '{username}'", ip_address=ip)
            self._json_response({"error": "Invalid credentials or account locked"}, 401)
            return
        self._set_session(user)
        log_action(user_id=user["id"], username=user["username"], action="login", resource_type="session", ip_address=ip)
        self._json_response({"ok": True, "message": "Login successful", "user": {k: user[k] for k in ("id", "username", "role", "display_name", "patient_id") if k in user}})

    def _api_logout(self):
        user = self._get_current_user()
        if user:
            log_action(user_id=user["id"], username=user["username"], action="logout", resource_type="session")
        self._clear_session()
        self._json_response({"message": "Logged out"})

    def _api_whoami(self):
        user = self._get_current_user()
        if user is None:
            self._json_response({"authenticated": False}, 200)
            return
        self._json_response({"authenticated": True, "user": {k: user[k] for k in ("id", "username", "role", "display_name", "patient_id") if k in user}})

    # Patient API
    def _api_list_patients(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        db = get_db()
        role = user["role"]
        if role in ("patient", "caregiver"):
            p = db.fetchone("SELECT * FROM patients WHERE id = ?", (user.get("patient_id"),))
            patients = [dict(p)] if p else []
        elif role in ("oncologist", "had_nurse"):
            patients = [dict(r) for r in db.fetchall("SELECT * FROM patients WHERE primary_oncologist_id = ? OR status = 'active'", (user["id"],))]
        else:
            patients = [dict(r) for r in db.fetchall("SELECT * FROM patients WHERE status = 'active'")]
        for p in patients:
            p["name"] = f"{p.get('first_name', '')} {p.get('last_name', '')}".strip()
            plan = db.fetchone("SELECT protocol_name, current_cycle, cycle_count AS total_cycles FROM treatment_plans WHERE patient_id = ? AND status = 'active' LIMIT 1", (p["id"],))
            if plan:
                p["treatment_protocol"] = plan["protocol_name"]
                p["current_cycle"] = f"Cycle {plan['current_cycle']} of {plan['total_cycles']}"
        log_action(user_id=user["id"], action="list_patients", resource_type="patient")
        self._json_response({"patients": patients})

    def _api_get_patient(self, pid):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        db = get_db()
        patient = db.fetchone("SELECT * FROM patients WHERE id = ?", (pid,))
        if patient is None:
            self._json_response({"error": "Patient not found"}, 404)
            return
        d = dict(patient)
        d["name"] = f"{d.get('first_name', '')} {d.get('last_name', '')}".strip()
        plan = db.fetchone("SELECT * FROM treatment_plans WHERE patient_id = ? AND status = 'active' ORDER BY created_at DESC LIMIT 1", (pid,))
        if plan:
            d["active_treatment_plan"] = dict(plan)
            d["treatment_protocol"] = plan["protocol_name"]
            d["current_cycle"] = f"Cycle {plan['current_cycle']} of {plan['cycle_count']}"
        d["recent_grades"] = [dict(g) for g in db.fetchall("SELECT * FROM toxicity_grades WHERE patient_id = ? ORDER BY created_at DESC LIMIT 10", (pid,))]
        d["pending_alerts"] = [dict(a) for a in db.fetchall("SELECT * FROM alerts WHERE patient_id = ? AND status = 'pending' ORDER BY created_at DESC", (pid,))]
        log_action(user_id=user["id"], action="view_patient", resource_type="patient", resource_id=int(pid))
        self._json_response(d)

    # Report API
    def _api_submit_report(self):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return

        data = self._read_json_body()
        pid = data.get("patient_id") or user.get("patient_id")
        if not pid:
            self._json_response({"error": "Missing required field: patient_id"}, 400)
            return

        db = get_db()
        engine = get_ctcae_engine()
        plan = db.fetchone(
            "SELECT * FROM treatment_plans WHERE patient_id = ? AND status = 'active' LIMIT 1",
            (pid,),
        )
        episode_id = plan["episode_id"] if plan else None
        plan_id = plan["id"] if plan else None

        # Determine symptoms to process
        symptoms_to_process = []
        primary_sid = data.get("symptom_id")
        primary_cat = data.get("symptom_category")
        primary_score = data.get("severity_score")

        if "symptoms" in data and isinstance(data["symptoms"], dict):
            symptoms_map = data["symptoms"]
            # If primary_sid was also provided, ensure it is first
            if primary_sid:
                score = primary_score if primary_score is not None else symptoms_map.get(primary_sid, 0)
                rule = engine.get_symptom_rule(primary_sid)
                cat = primary_cat or (rule.get("category") if rule else "general")
                gi = dict(data.get("grading_inputs") or {})
                if score is not None:
                    gi["severity_score"] = score
                if data.get("lab_values"):
                    gi.update(data["lab_values"])
                symptoms_to_process.append((primary_sid, cat, score, gi))

            for sid, score in symptoms_map.items():
                if sid == primary_sid:
                    continue
                if score is not None and score > 0:
                    rule = engine.get_symptom_rule(sid)
                    cat = rule.get("category", "general") if rule else "general"
                    symptoms_to_process.append((sid, cat, score, {"severity_score": score}))

            if not symptoms_to_process:
                # If no symptoms scored > 0 and no primary_sid, record standard check
                fallback_sid = primary_sid or "nausea"
                rule = engine.get_symptom_rule(fallback_sid)
                cat = primary_cat or (rule.get("category") if rule else "general")
                symptoms_to_process.append((fallback_sid, cat, 0, {"severity_score": 0}))
        else:
            # Flat payload
            if not primary_sid:
                self._json_response({"error": "Missing required field: symptom_id"}, 400)
                return
            rule = engine.get_symptom_rule(primary_sid)
            cat = primary_cat or (rule.get("category") if rule else "general")
            gi = dict(data.get("grading_inputs") or {})
            if primary_score is not None:
                gi["severity_score"] = primary_score
            if data.get("lab_values"):
                gi.update(data["lab_values"])
            symptoms_to_process.append((primary_sid, cat, primary_score, gi))

        all_grades = []
        primary_gr = None
        primary_rid = None
        max_alert = None

        for idx, (sid, cat, score, gi) in enumerate(symptoms_to_process):
            rule = engine.get_symptom_rule(sid)
            cat = cat or (rule.get("category") if rule else "general")
            disp_name = rule.get("display_name", sid) if rule else sid

            cur = db.execute(
                "INSERT INTO toxicity_reports (patient_id, episode_id, treatment_plan_id, reporter_id, symptom_id, symptom_category, severity_score, lab_values, notes, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')",
                (pid, episode_id, plan_id, user["id"], sid, cat, score, json.dumps(data.get("lab_values", {})) if data.get("lab_values") else None, data.get("notes", "")),
            )
            db.commit()
            rid = cur.lastrowid
            if primary_rid is None:
                primary_rid = rid

            gr = engine.grade_and_save(rid, pid, sid, gi)
            if primary_gr is None:
                primary_gr = gr

            all_grades.append({
                "symptom_id": sid,
                "term": disp_name,
                "grade": gr.get("grade", 0),
                "provisional": gr.get("provisional", False),
                "criteria": gr.get("criteria", ""),
            })

            alert = None
            if gr.get("grade") and gr["grade"] >= 2:
                alert = get_alert_engine().create_alert_for_grade(
                    grade_id=gr.get("grade_id"),
                    patient_id=pid,
                    report_id=rid,
                    grade=gr["grade"],
                    symptom_id=sid,
                    display_name=disp_name,
                    reporter_id=user["id"],
                )
                if max_alert is None or alert.get("tier") == "emergency":
                    max_alert = alert

            db.execute(
                "INSERT INTO timeline_events (patient_id, episode_id, event_type, title, description, created_by) VALUES (?, ?, 'report', ?, ?, ?)",
                (pid, episode_id, f"Toxicity report: {disp_name}", f"Grade {gr.get('grade', 'N/A')} - {gr.get('criteria', '')}", user["id"]),
            )
            db.commit()

            log_action(
                user_id=user["id"],
                action="submit_report",
                resource_type="toxicity_report",
                resource_id=rid,
                details=f"Report for {sid} on patient {pid}, grade {gr.get('grade')}",
            )

        # Guidance lookup
        guidance_text = None
        gp = config.get("guidance", "path", default=str(get_bundle_dir() / "guidance" / "guidance.json"))
        try:
            if Path(gp).exists():
                with open(gp, "r", encoding="utf-8") as f:
                    gd = json.load(f)
                highest_symptom = symptoms_to_process[0][0]
                highest_grade = primary_gr.get("grade") if primary_gr else 1
                sg = gd.get("symptoms", {}).get(highest_symptom)
                if sg:
                    g_info = sg.get("grades", {}).get(str(highest_grade))
                    if g_info:
                        guidance_text = g_info.get("patient_action", g_info.get("clinical_action", ""))
        except Exception:
            pass

        self._json_response({
            "ok": True,
            "message": "Report submitted and graded",
            "report_id": primary_rid,
            "grading": primary_gr,
            "grades": all_grades,
            "alert": max_alert,
            "guidance": guidance_text,
        }, 201)

    def _api_list_reports(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        pid = params.get("patient_id", [None])[0]
        if not pid:
            self._json_response({"error": "patient_id parameter required"}, 400)
            return
        reports = get_db().fetchall("SELECT * FROM toxicity_reports WHERE patient_id = ? ORDER BY report_date DESC", (pid,))
        log_action(user_id=user["id"], action="list_reports", resource_type="toxicity_report", details=f"Patient {pid}")
        self._json_response({"reports": [dict(r) for r in reports]})

    def _api_list_grades(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        pid = params.get("patient_id", [None])[0]
        if not pid:
            self._json_response({"error": "patient_id parameter required"}, 400)
            return
        grades = get_db().fetchall("SELECT * FROM toxicity_grades WHERE patient_id = ? ORDER BY created_at DESC", (pid,))
        self._json_response({"grades": [dict(g) for g in grades]})

    # Alert API
    def _api_list_alerts(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        ae = get_alert_engine()
        pid = params.get("patient_id", [None])[0]
        status = params.get("status", [None])[0]
        if user["role"] in ("patient", "caregiver"):
            pid = user.get("patient_id")
        alerts = ae.get_alerts(patient_id=int(pid) if pid else None, status=status,
                               assigned_to=user["id"] if user["role"] in ("oncologist", "had_nurse") else None)
        self._json_response({"alerts": alerts})

    def _api_acknowledge_alert(self, aid):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        if get_alert_engine().acknowledge_alert(int(aid), user["id"]):
            self._json_response({"message": "Alert acknowledged"})
        else:
            self._json_response({"error": "Alert not found"}, 404)

    # Timeline API
    def _api_get_timeline(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        pid = params.get("patient_id", [None])[0]
        if not pid and user["role"] in ("patient", "caregiver"):
            pid = user.get("patient_id")
        eid = params.get("episode_id", [None])[0]
        etype = params.get("type", [None])[0]
        limit = int(params.get("limit", [50])[0])
        conds, qparams = [], []
        if pid:
            conds.append("patient_id = ?")
            qparams.append(int(pid))
        if eid:
            conds.append("episode_id = ?")
            qparams.append(int(eid))
        if etype:
            conds.append("event_type = ?")
            qparams.append(etype)
        wc = " AND ".join(conds) if conds else "1=1"
        events = get_db().fetchall(
            f"SELECT * FROM timeline_events WHERE {wc} ORDER BY event_date DESC LIMIT ?",
            tuple(qparams) + (limit,),
        )
        dict_events = []
        for e in events:
            d = dict(e)
            d["type"] = d.get("event_type", "event")
            d["created_at"] = d.get("event_date", d.get("created_at"))
            dict_events.append(d)
        self._json_response({"events": dict_events})

    # Treatment Plan API
    def _api_get_treatment_plan(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        pid = params.get("patient_id", [None])[0]
        if not pid:
            self._json_response({"error": "patient_id parameter required"}, 400)
            return
        plans = get_db().fetchall("SELECT * FROM treatment_plans WHERE patient_id = ? ORDER BY created_at DESC", (pid,))
        self._json_response({"treatment_plans": [dict(p) for p in plans]})

    # Message API
    def _api_send_message(self):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        data = self._read_json_body()
        body = data.get("body", "").strip()
        if not body:
            self._json_response({"error": "Message body required"}, 400)
            return
        db = get_db()
        cur = db.execute("INSERT INTO messages (sender_id, recipient_id, recipient_role, patient_id, subject, body, message_type) VALUES (?, ?, ?, ?, ?, ?, ?)",
                         (user["id"], data.get("recipient_id"), data.get("recipient_role"), data.get("patient_id"), data.get("subject", ""), body, data.get("message_type", "direct")))
        db.commit()
        log_action(user_id=user["id"], action="send_message", resource_type="message", resource_id=cur.lastrowid)
        self._json_response({"message": "Message sent", "message_id": cur.lastrowid}, 201)

    def _api_list_messages(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        db = get_db()
        pid = params.get("patient_id", [None])[0]
        limit = int(params.get("limit", [50])[0])
        if pid:
            msgs = db.fetchall("SELECT * FROM messages WHERE patient_id = ? AND (recipient_id = ? OR sender_id = ? OR recipient_role = ?) ORDER BY created_at DESC LIMIT ?",
                               (int(pid), user["id"], user["id"], user["role"], limit))
        else:
            msgs = db.fetchall("SELECT * FROM messages WHERE recipient_id = ? OR sender_id = ? OR recipient_role = ? ORDER BY created_at DESC LIMIT ?",
                               (user["id"], user["id"], user["role"], limit))
        self._json_response({"messages": [dict(m) for m in msgs]})

    # Export API
    def _api_export_summary(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        pid = params.get("patient_id", [None])[0]
        if not pid:
            self._json_response({"error": "patient_id parameter required"}, 400)
            return
        db = get_db()
        patient = db.fetchone("SELECT * FROM patients WHERE id = ?", (pid,))
        if patient is None:
            self._json_response({"error": "Patient not found"}, 404)
            return
        grades = db.fetchall("SELECT * FROM toxicity_grades WHERE patient_id = ? ORDER BY created_at DESC", (pid,))
        plans = db.fetchall("SELECT * FROM treatment_plans WHERE patient_id = ? ORDER BY created_at DESC", (pid,))
        summary = {
            "patient": {"mrn": patient["mrn"], "name": f"{patient['first_name']} {patient['last_name']}",
                        "date_of_birth": patient["date_of_birth"], "cancer_type": patient["cancer_type"], "cancer_stage": patient["cancer_stage"]},
            "treatment_plans": [{"protocol": p["protocol_name"], "regimen": p["regimen"], "current_cycle": p["current_cycle"], "total_cycles": p["cycle_count"], "status": p["status"]} for p in plans],
            "toxicity_summary": {},
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "generated_by": user["username"],
        }
        for g in grades:
            s = g["symptom_id"]
            if s not in summary["toxicity_summary"]:
                summary["toxicity_summary"][s] = {"latest_grade": g["grade"], "latest_criteria": g["criteria"], "provisional": bool(g["provisional"]), "history": []}
            summary["toxicity_summary"][s]["history"].append({"grade": g["grade"], "criteria": g["criteria"], "date": g["created_at"]})
        log_action(user_id=user["id"], action="export_summary", resource_type="patient", resource_id=int(pid))
        self._json_response(summary)

    # Audit API
    def _api_audit_log(self, params):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        if user["role"] != "admin":
            self._json_response({"error": "Admin access required"}, 403)
            return
        limit = int(params.get("limit", [100])[0])
        offset = int(params.get("offset", [0])[0])
        action_f = params.get("action", [None])[0]
        self._json_response({"audit_log": get_audit_log(action=action_f, limit=limit, offset=offset)})

    # Symptom & Guidance API
    def _api_list_symptoms(self):
        self._json_response({"symptoms": get_ctcae_engine().list_symptoms()})

    def _api_get_guidance(self, params):
        sid = params.get("symptom_id", [None])[0]
        grade = params.get("grade", [None])[0]
        if not sid:
            self._json_response({"error": "symptom_id parameter required"}, 400)
            return
        gp = config.get("guidance", "path", default="guidance/guidance.json")
        try:
            with open(gp, "r", encoding="utf-8") as f:
                gd = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            self._json_response({"error": "Guidance data not available"}, 500)
            return
        sg = gd.get("symptoms", {}).get(sid)
        if sg is None:
            self._json_response({"error": f"No guidance found for symptom: {sid}"}, 404)
            return
        if grade:
            gg = sg.get("grades", {}).get(str(grade))
            if gg:
                self._json_response({"symptom_id": sid, "display_name": sg.get("display_name"), "grade": int(grade), "guidance": gg})
            else:
                self._json_response({"error": f"No guidance for grade {grade}"}, 404)
        else:
            self._json_response({"symptom_id": sid, "display_name": sg.get("display_name"), "grades": sg.get("grades", {})})

    # Chat API
    def _api_chat(self):
        user = self._get_current_user()
        if user is None:
            self._json_response({"error": "Authentication required"}, 401)
            return
        data = self._read_json_body()
        msg = data.get("message", "").strip()
        if not msg:
            self._json_response({"error": "Message required"}, 400)
            return
        aname = config.get("ai", "adapter", default="stub")
        adapter = AI_ADAPTERS.get(aname, StubAdapter)()
        ctx = {"patient_id": data.get("patient_id"), "user_role": user["role"], "history": data.get("history", []), "symptoms": data.get("symptoms", [])}
        result = adapter.chat(msg, ctx)
        log_action(user_id=user["id"], action="chat", resource_type="ai_chat", details=f"Adapter: {aname}, len: {len(msg)}")
        self._json_response(result)

    def log_message(self, format, *args):
        if config.get("debug", default=False):
            super().log_message(format, *args)


def run_server(host=None, port=None, debug=None):
    if host is None:
        host = config.get("host", default="127.0.0.1")
    if port is None:
        port = config.get("port", default=8080)
    if debug is None:
        debug = config.get("debug", default=False)
    db = get_db()
    print(f"[app] Database initialized at {config.get('database', 'path')}")
    if config.get("demo", "auto_seed", default=True):
        seed_demo_data()
    server = http.server.ThreadingHTTPServer((host, port), HADRequestHandler)
    print(f"""
+------------------------------------------------------------------+
|                    HAD Digital MVP                                |
|          Hospitalisation a Domicile Platform                      |
+------------------------------------------------------------------+
|  Server:  http://{host}:{port}
|  Debug:   {debug}
|  DB:      SQLite (WAL mode)
+------------------------------------------------------------------+
|  Demo Users:
|    admin / admin123         (Admin)
|    dr.martin / demo123      (Oncologist)
|    inf.moret / demo123      (HAD Nurse)
|    patient.durand / demo123 (Patient)
+------------------------------------------------------------------+
""")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n[app] Shutting down...")
        server.shutdown()


if __name__ == "__main__":
    import argparse
    import threading
    try:
        import webview
        HAS_WEBVIEW = True
    except ImportError:
        HAS_WEBVIEW = False

    parser = argparse.ArgumentParser(description="HAD Digital MVP Server")
    parser.add_argument("--host", default=None, help="Host to bind to")
    parser.add_argument("--port", type=int, default=None, help="Port to bind to")
    parser.add_argument("--debug", action="store_true", help="Enable debug mode")
    args = parser.parse_args()
    
    if args.debug:
        os.environ["HAD_DEBUG"] = "true"
        
    host = args.host if args.host else config.get("host", default="127.0.0.1")
    port = args.port if args.port else config.get("port", default=8080)

    if HAS_WEBVIEW and os.environ.get("HAD_NO_WINDOW") != "1":
        print("[app] Launching native window via pywebview...")
        t = threading.Thread(target=run_server, args=(host, port, args.debug), daemon=True)
        t.start()
        # Give server a moment to start up
        import time
        time.sleep(1)
        webview.create_window("HAD Digital MVP", f"http://{host}:{port}", width=1200, height=800)
        webview.start()
    else:
        if HAS_WEBVIEW:
            print("[app] HAD_NO_WINDOW=1. Running headless server...")
        else:
            print("[app] pywebview not installed. Running headless server...")
        run_server(host=host, port=port, debug=args.debug)
