"""Calm native ARMOR OS Academy dashboard for SHiRE Unified Learning 0003."""
from __future__ import annotations

import json
import subprocess
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any

from PyQt5.QtCore import QThread, QTimer, Qt, pyqtSignal
from PyQt5.QtWidgets import (
    QFrame, QHBoxLayout, QLabel, QProgressBar, QPushButton, QScrollArea,
    QSizePolicy, QStackedWidget, QVBoxLayout, QWidget,
)

from modules.unified_learning import UnifiedLearningDialog

SERVICE_URL = "http://127.0.0.1:8792"
PROTECTED_SKILL_TOTAL = 371
PROTECTED_FILES = (
    Path("/home/shire3d/ARMOR/data/academy/design_mastery/runtime/practice_state.json"),
    Path("/home/shire3d/ARMOR/data/academy/design_mastery/runtime/autopilot_state.json"),
    Path("/home/shire3d/ARMOR/data/academy/design_mastery/runtime/commercial_design_sprint.json"),
)


class DashboardWorker(QThread):
    completed = pyqtSignal(bool, object)

    def run(self) -> None:
        try:
            with urllib.request.urlopen(SERVICE_URL + "/api/dashboard", timeout=8) as response:
                payload = json.loads(response.read().decode("utf-8"))
            self.completed.emit(bool(payload.get("ok")), payload)
        except urllib.error.HTTPError as exc:
            self.completed.emit(False, {"error": f"HTTP {exc.code}"})
        except Exception as exc:
            self.completed.emit(False, {"error": str(exc)})


def _walk(value: Any):
    if isinstance(value, dict):
        for key, child in value.items():
            yield str(key), child
            yield from _walk(child)
    elif isinstance(value, list):
        for child in value:
            yield from _walk(child)


def _find_first(documents: list[Any], names: tuple[str, ...], default: Any = None) -> Any:
    wanted = {name.lower() for name in names}
    for document in documents:
        for key, value in _walk(document):
            if key.lower() in wanted and value not in (None, ""):
                return value
    return default


def _bool_text(value: Any) -> str:
    if isinstance(value, bool):
        return "Yes" if value else "No"
    return str(value) if value not in (None, "") else "Unknown"


def protected_snapshot() -> dict[str, Any]:
    documents: list[Any] = []
    for path in PROTECTED_FILES:
        try:
            documents.append(json.loads(path.read_text(encoding="utf-8")))
        except Exception:
            continue

    current = _find_first(documents, ("current_skill", "current_skill_id", "selected_skill", "active_skill"), "Not reported")
    if isinstance(current, dict):
        current = current.get("id") or current.get("skill_id") or current.get("name") or "Not reported"
    certified = _find_first(documents, ("knowledge_certified", "current_knowledge_certified"), False)
    evidence_pending = _find_first(documents, ("safe_cadquery_evidence_pending", "tool_evidence_pending", "evidence_pending"), None)
    remaining = _find_first(documents, ("safe_cadquery_candidates_remaining", "remaining_candidates", "candidates_remaining"), None)
    completed = _find_first(documents, ("certified_skill_count", "completed_skill_count", "skills_completed", "certified_count"), None)
    paused = _find_first(documents, ("paused_for_review", "scheduler_paused", "review_required"), None)

    try:
        timer_state = subprocess.run(
            ["systemctl", "is-active", "shire-academy-safe-autonomous.timer"],
            capture_output=True, text=True, timeout=2, check=False,
        ).stdout.strip() or "unknown"
    except Exception:
        timer_state = "unknown"

    stage = "Safe CadQuery evidence" if evidence_pending is True else "Knowledge and assessment"
    running = timer_state == "active" and paused is not True
    completed_int = int(completed) if isinstance(completed, (int, float)) else None
    progress = round((completed_int / PROTECTED_SKILL_TOTAL) * 100) if completed_int is not None else None
    return {
        "current_skill": str(current),
        "knowledge_certified": bool(certified),
        "evidence_pending": evidence_pending,
        "remaining_candidates": remaining,
        "completed_skills": completed_int,
        "curriculum_progress": progress,
        "stage": stage,
        "timer_state": timer_state,
        "running": running,
        "paused_for_review": paused is True,
    }


def clear_layout(layout) -> None:
    while layout.count():
        item = layout.takeAt(0)
        widget = item.widget()
        child_layout = item.layout()
        if child_layout is not None:
            clear_layout(child_layout)
        if widget is not None:
            widget.setParent(None)
            widget.deleteLater()


class InfoCard(QFrame):
    def __init__(self, title: str, value: str = "—", detail: str = "", parent=None):
        super().__init__(parent)
        self.setObjectName("infoCard")
        layout = QVBoxLayout(self)
        layout.setContentsMargins(16, 14, 16, 14)
        layout.setSpacing(6)
        title_label = QLabel(title.upper())
        title_label.setObjectName("cardLabel")
        self.value_label = QLabel(value)
        self.value_label.setObjectName("cardValue")
        self.value_label.setWordWrap(True)
        self.detail_label = QLabel(detail)
        self.detail_label.setObjectName("cardDetail")
        self.detail_label.setWordWrap(True)
        layout.addWidget(title_label)
        layout.addWidget(self.value_label)
        layout.addWidget(self.detail_label)

    def set_content(self, value: str, detail: str = "") -> None:
        self.value_label.setText(value)
        self.detail_label.setText(detail)


class UnifiedAcademyDashboard(QWidget):
    NAV_ITEMS = ("Overview", "Current Learning", "Curriculum", "Evidence & Results", "Reviews", "Settings")

    def __init__(self, host=None):
        super().__init__(host)
        self.host = host
        self.worker: DashboardWorker | None = None
        self.dashboard_data: dict[str, Any] = {}
        self.setObjectName("unifiedAcademyDashboard")
        self.setStyleSheet(self._stylesheet())
        self._build()
        self.timer = QTimer(self)
        self.timer.setInterval(5000)
        self.timer.timeout.connect(self.refresh_data)
        self.timer.start()
        QTimer.singleShot(0, self.refresh_data)

    def _build(self) -> None:
        outer = QHBoxLayout(self)
        outer.setContentsMargins(0, 0, 0, 0)
        outer.setSpacing(0)

        sidebar = QFrame()
        sidebar.setObjectName("academySidebar")
        sidebar.setFixedWidth(220)
        side = QVBoxLayout(sidebar)
        side.setContentsMargins(18, 22, 18, 18)
        side.setSpacing(8)
        brand = QLabel("SHiRE\nACADEMY")
        brand.setObjectName("academyBrand")
        side.addWidget(brand)
        self.side_status = QLabel("ACADEMY CHECKING")
        self.side_status.setObjectName("statusPill")
        side.addWidget(self.side_status)
        side.addSpacing(18)
        self.nav_buttons: list[QPushButton] = []
        for index, name in enumerate(self.NAV_ITEMS):
            button = QPushButton(name)
            button.setCheckable(True)
            button.setObjectName("navButton")
            button.clicked.connect(lambda checked=False, i=index: self.select_page(i))
            self.nav_buttons.append(button)
            side.addWidget(button)
        side.addStretch(1)
        version = QLabel("Unified Learning 0003\nLocalhost only · SHiREVault evidence")
        version.setObjectName("sidebarFoot")
        version.setWordWrap(True)
        side.addWidget(version)
        outer.addWidget(sidebar)

        content = QFrame()
        content.setObjectName("academyContent")
        content_layout = QVBoxLayout(content)
        content_layout.setContentsMargins(24, 20, 24, 22)
        content_layout.setSpacing(16)

        header = QHBoxLayout()
        title_box = QVBoxLayout()
        title = QLabel("Academy Overview")
        title.setObjectName("pageTitle")
        subtitle = QLabel("One calm command centre for protected Academy learning and real custom learning jobs.")
        subtitle.setObjectName("pageSubtitle")
        subtitle.setWordWrap(True)
        title_box.addWidget(title)
        title_box.addWidget(subtitle)
        header.addLayout(title_box, 1)
        self.header_status = QLabel("Checking services")
        self.header_status.setObjectName("statusPill")
        header.addWidget(self.header_status)
        learn = QPushButton("＋ New Learning Request")
        learn.setObjectName("primaryButton")
        learn.clicked.connect(self.open_learning_request)
        header.addWidget(learn)
        content_layout.addLayout(header)

        self.notice = QLabel("")
        self.notice.setObjectName("noticeBar")
        self.notice.setWordWrap(True)
        self.notice.hide()
        content_layout.addWidget(self.notice)

        self.stack = QStackedWidget()
        self.stack.addWidget(self._overview_page())
        self.stack.addWidget(self._current_page())
        self.stack.addWidget(self._curriculum_page())
        self.stack.addWidget(self._evidence_page())
        self.stack.addWidget(self._reviews_page())
        self.stack.addWidget(self._settings_page())
        content_layout.addWidget(self.stack, 1)
        outer.addWidget(content, 1)
        self.select_page(0)

    def _scroll_page(self, inner: QWidget) -> QScrollArea:
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QFrame.NoFrame)
        scroll.setWidget(inner)
        return scroll

    def _section(self, title: str, text: str = "") -> tuple[QFrame, QVBoxLayout]:
        frame = QFrame()
        frame.setObjectName("sectionPanel")
        layout = QVBoxLayout(frame)
        layout.setContentsMargins(16, 15, 16, 15)
        layout.setSpacing(9)
        heading = QLabel(title)
        heading.setObjectName("sectionTitle")
        layout.addWidget(heading)
        if text:
            body = QLabel(text)
            body.setObjectName("sectionText")
            body.setWordWrap(True)
            layout.addWidget(body)
        return frame, layout

    def _overview_page(self) -> QWidget:
        inner = QWidget()
        layout = QVBoxLayout(inner)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(14)

        cards = QHBoxLayout()
        cards.setSpacing(12)
        self.current_skill_card = InfoCard("Current Skill")
        self.stage_card = InfoCard("Current Stage")
        self.knowledge_card = InfoCard("Knowledge Progress")
        self.curriculum_card = InfoCard("Overall Curriculum")
        for card in (self.current_skill_card, self.stage_card, self.knowledge_card, self.curriculum_card):
            card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
            cards.addWidget(card)
        layout.addLayout(cards)

        row = QHBoxLayout()
        row.setSpacing(12)
        pipeline_frame, pipeline = self._section("Learning Pipeline")
        self.pipeline_text = QLabel("Checking saved plans and learning jobs…")
        self.pipeline_text.setObjectName("sectionText")
        self.pipeline_text.setWordWrap(True)
        pipeline.addWidget(self.pipeline_text)
        row.addWidget(pipeline_frame, 3)

        guard_frame, guard = self._section("Guardrails")
        guard_text = QLabel(
            "No automatic certification\nNo counted Academy attempts\nNo automatic Forge unlock\n"
            "No protected 371-skill writes\nNo false research, tool or physical-test claims"
        )
        guard_text.setObjectName("guardrailText")
        guard_text.setWordWrap(True)
        guard.addWidget(guard_text)
        row.addWidget(guard_frame, 2)
        layout.addLayout(row)

        bottom = QHBoxLayout()
        bottom.setSpacing(12)
        activity_frame, activity = self._section("Recent Activity")
        self.activity_text = QLabel("No activity loaded yet.")
        self.activity_text.setObjectName("sectionText")
        self.activity_text.setWordWrap(True)
        activity.addWidget(self.activity_text)
        bottom.addWidget(activity_frame, 3)

        notifications_frame, notifications = self._section("Notifications")
        self.notifications_text = QLabel("No review alerts loaded yet.")
        self.notifications_text.setObjectName("sectionText")
        self.notifications_text.setWordWrap(True)
        notifications.addWidget(self.notifications_text)
        bottom.addWidget(notifications_frame, 2)
        layout.addLayout(bottom)

        quick_frame, quick = self._section("Quick Actions")
        quick_row = QHBoxLayout()
        new_request = QPushButton("New Learning Request")
        new_request.setObjectName("primaryButton")
        new_request.clicked.connect(self.open_learning_request)
        refresh = QPushButton("Refresh Dashboard")
        refresh.clicked.connect(self.refresh_data)
        current = QPushButton("View Current Learning")
        current.clicked.connect(lambda: self.select_page(1))
        quick_row.addWidget(new_request)
        quick_row.addWidget(refresh)
        quick_row.addWidget(current)
        quick_row.addStretch(1)
        quick.addLayout(quick_row)
        layout.addWidget(quick_frame)
        layout.addStretch(1)
        return self._scroll_page(inner)

    def _list_page(self, title: str, intro: str) -> tuple[QWidget, QVBoxLayout]:
        inner = QWidget()
        layout = QVBoxLayout(inner)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(12)
        frame, section = self._section(title, intro)
        layout.addWidget(frame)
        layout.addStretch(1)
        return self._scroll_page(inner), section

    def _current_page(self) -> QWidget:
        page, section = self._list_page("Current Learning", "Real custom learning requests and their latest persistent job state.")
        self.current_learning_text = QLabel("No learning jobs loaded yet.")
        self.current_learning_text.setObjectName("sectionText")
        self.current_learning_text.setWordWrap(True)
        section.addWidget(self.current_learning_text)
        return page

    def _curriculum_page(self) -> QWidget:
        page, section = self._list_page("Curriculum", "Read-only view of the protected 371-skill Academy curriculum.")
        self.curriculum_text = QLabel("Protected curriculum state is being read without modification.")
        self.curriculum_text.setObjectName("sectionText")
        self.curriculum_text.setWordWrap(True)
        section.addWidget(self.curriculum_text)
        self.curriculum_progress = QProgressBar()
        self.curriculum_progress.setRange(0, 100)
        self.curriculum_progress.setValue(0)
        section.addWidget(self.curriculum_progress)
        return page

    def _evidence_page(self) -> QWidget:
        page, section = self._list_page("Evidence & Results", "Persistent artifacts, independent assessments and preserved failures remain on SHiREVault.")
        self.evidence_text = QLabel("Evidence summary will appear here.")
        self.evidence_text.setObjectName("sectionText")
        self.evidence_text.setWordWrap(True)
        section.addWidget(self.evidence_text)
        return page

    def _reviews_page(self) -> QWidget:
        page, section = self._list_page("Reviews", "Jobs pause here when an assessment fails or manual/professional judgement is required.")
        self.reviews_text = QLabel("No review items loaded yet.")
        self.reviews_text.setObjectName("sectionText")
        self.reviews_text.setWordWrap(True)
        section.addWidget(self.reviews_text)
        return page

    def _settings_page(self) -> QWidget:
        page, section = self._list_page("Settings", "Unified Learning remains private, local and evidence-first.")
        details = QLabel(
            "Unified service: http://127.0.0.1:8792\n"
            "Academy: http://127.0.0.1:8790\n"
            "Plans: /SHiREVault/SHiREAcademy/UnifiedLearning/Plans\n"
            "Prompt limit: 1,000 characters\n"
            "Sentinel: managed separately and never modified by Academy updates"
        )
        details.setObjectName("sectionText")
        details.setWordWrap(True)
        section.addWidget(details)
        return page

    def select_page(self, index: int) -> None:
        self.stack.setCurrentIndex(index)
        for button_index, button in enumerate(self.nav_buttons):
            button.setChecked(button_index == index)

    def open_learning_request(self) -> None:
        dialog = UnifiedLearningDialog("", self)
        if dialog.exec_() == dialog.Accepted:
            job = getattr(dialog, "started_job", {}) or {}
            job_id = job.get("job_id", "Persistent learning job created")
            self.show_notice(f"Learning started: {job_id}. The plan window closed after SHiREVault confirmed the job.")
            QTimer.singleShot(150, self.refresh_data)

    def show_notice(self, message: str, error: bool = False) -> None:
        self.notice.setText(message)
        self.notice.setProperty("error", error)
        self.notice.style().unpolish(self.notice)
        self.notice.style().polish(self.notice)
        self.notice.show()
        if not error:
            QTimer.singleShot(9000, self.notice.hide)

    def refresh_data(self) -> None:
        if self.worker is not None and self.worker.isRunning():
            return
        self.worker = DashboardWorker(self)
        self.worker.completed.connect(self._dashboard_loaded)
        self.worker.start()

    def _dashboard_loaded(self, ok: bool, payload: object) -> None:
        data = payload if isinstance(payload, dict) else {}
        snapshot = protected_snapshot()
        service_ok = ok and bool(data.get("ok"))
        self.dashboard_data = data
        state_text = "ACADEMY RUNNING" if snapshot["running"] and service_ok else "ACADEMY PAUSED / REVIEW"
        if not service_ok:
            state_text = "UNIFIED SERVICE OFFLINE"
        self.side_status.setText(state_text)
        self.header_status.setText(state_text.title())

        self.current_skill_card.set_content(snapshot["current_skill"], "Protected Academy state · read only")
        evidence = snapshot["evidence_pending"]
        stage_detail = "Evidence pending" if evidence is True else "No evidence flag reported"
        self.stage_card.set_content(snapshot["stage"], stage_detail)
        self.knowledge_card.set_content("Certified" if snapshot["knowledge_certified"] else "In progress", "Knowledge certification is displayed only; Unified Learning cannot alter it")

        completed = snapshot["completed_skills"]
        remaining = snapshot["remaining_candidates"]
        if completed is not None:
            overall = f"{completed} / {PROTECTED_SKILL_TOTAL}"
        else:
            overall = f"{PROTECTED_SKILL_TOTAL} protected skills"
        detail = f"Safe CadQuery candidates remaining: {remaining}" if remaining is not None else "No protected curriculum writes"
        self.curriculum_card.set_content(overall, detail)
        self.curriculum_text.setText(
            f"Current skill: {snapshot['current_skill']}\nCurrent stage: {snapshot['stage']}\n"
            f"Knowledge certified: {_bool_text(snapshot['knowledge_certified'])}\n"
            f"Safe evidence pending: {_bool_text(snapshot['evidence_pending'])}\n"
            f"Scheduler timer: {snapshot['timer_state']}\n{detail}"
        )
        self.curriculum_progress.setValue(snapshot["curriculum_progress"] or 0)
        self.curriculum_progress.setFormat(
            f"{snapshot['curriculum_progress']}%" if snapshot["curriculum_progress"] is not None else "Protected progress available when reported"
        )

        summary = data.get("summary") if isinstance(data.get("summary"), dict) else {}
        plans = data.get("plans") if isinstance(data.get("plans"), list) else []
        reviews = data.get("reviews") if isinstance(data.get("reviews"), list) else []
        activity = data.get("recent_activity") if isinstance(data.get("recent_activity"), list) else []
        self.pipeline_text.setText(
            f"Saved plans: {summary.get('plans', 0)}\n"
            f"Active jobs: {summary.get('active_jobs', 0)}\n"
            f"Completed knowledge jobs: {summary.get('completed_jobs', 0)}\n"
            f"Waiting for review: {summary.get('review_jobs', 0)}"
        )
        self.current_learning_text.setText(self._format_plans(plans))
        self.activity_text.setText(self._format_activity(activity))
        self.notifications_text.setText(self._format_reviews(reviews))
        self.reviews_text.setText(self._format_reviews(reviews))
        self.evidence_text.setText(
            f"Plans root: {data.get('plans_root', '/SHiREVault/SHiREAcademy/UnifiedLearning/Plans')}\n"
            f"Plans with persistent evidence: {summary.get('plans', 0)}\n"
            f"Completed knowledge jobs: {summary.get('completed_jobs', 0)}\n"
            f"Preserved review/failure jobs: {summary.get('review_jobs', 0)}\n\n"
            "Every autonomous stage keeps its artifact, writer provenance and separate assessment. Failures are not overwritten."
        )
        if not service_ok:
            self.show_notice("Unified Learning service could not be read: " + str(data.get("error") or "unknown error"), True)

    @staticmethod
    def _format_plans(plans: list[dict[str, Any]]) -> str:
        if not plans:
            return "No custom learning plans yet. Use New Learning Request to create one."
        rows = []
        for plan in plans[:12]:
            job = plan.get("latest_job") if isinstance(plan.get("latest_job"), dict) else {}
            rows.append(
                f"• {plan.get('title') or plan.get('plan_id')}\n"
                f"  Plan: {plan.get('plan_id')} · Revision {plan.get('revision', 1)}\n"
                f"  Status: {job.get('status') or plan.get('status') or 'saved'}"
            )
        return "\n\n".join(rows)

    @staticmethod
    def _format_activity(activity: list[dict[str, Any]]) -> str:
        if not activity:
            return "No recent Unified Learning activity."
        return "\n\n".join(
            f"• {item.get('title') or item.get('job_id') or 'Learning activity'}\n  {item.get('status', 'unknown')} · {item.get('updated_at') or item.get('created_at') or ''}"
            for item in activity[:8]
        )

    @staticmethod
    def _format_reviews(reviews: list[dict[str, Any]]) -> str:
        if not reviews:
            return "No failed assessments or manual review gates are waiting."
        return "\n\n".join(
            f"• {item.get('title') or item.get('job_id')}\n  {item.get('status')} · {item.get('job_id')}"
            for item in reviews[:10]
        )

    @staticmethod
    def _stylesheet() -> str:
        return """
        QWidget#unifiedAcademyDashboard { background: #080b12; color: #eef2f8; font-family: 'DejaVu Sans'; }
        QFrame#academySidebar { background: #0b1019; border-right: 1px solid #242d3b; }
        QFrame#academyContent { background: #080b12; }
        QLabel#academyBrand { color: #ffffff; font-size: 20px; font-weight: 900; letter-spacing: 2px; }
        QLabel#sidebarFoot { color: #718096; font-size: 10px; }
        QLabel#pageTitle { color: #ffffff; font-size: 25px; font-weight: 900; }
        QLabel#pageSubtitle, QLabel#sectionText { color: #aeb9ca; font-size: 12px; line-height: 1.45; }
        QLabel#statusPill { background: #14231e; color: #a9d7c5; border: 1px solid #315c4d; border-radius: 9px; padding: 6px 9px; font-size: 10px; font-weight: 800; }
        QPushButton#navButton { text-align: left; padding: 10px 11px; color: #aeb9ca; background: transparent; border: 1px solid transparent; border-radius: 8px; font-weight: 700; }
        QPushButton#navButton:hover { background: #111824; color: #ffffff; }
        QPushButton#navButton:checked { background: #172131; color: #ffffff; border-color: #314056; }
        QPushButton { background: #151d2a; color: #eef2f8; border: 1px solid #3a4659; border-radius: 8px; padding: 9px 13px; font-weight: 750; }
        QPushButton:hover { background: #1d2839; }
        QPushButton#primaryButton { background: #18372d; border-color: #407b65; color: #f4fff9; }
        QFrame#infoCard, QFrame#sectionPanel { background: #0d121c; border: 1px solid #283244; border-radius: 11px; }
        QLabel#cardLabel { color: #77879b; font-size: 9px; font-weight: 900; letter-spacing: 1px; }
        QLabel#cardValue { color: #f7f9fc; font-size: 16px; font-weight: 850; }
        QLabel#cardDetail { color: #91a0b3; font-size: 10px; }
        QLabel#sectionTitle { color: #f6f8fc; font-size: 14px; font-weight: 850; }
        QLabel#guardrailText { color: #b8d2c8; font-size: 11px; line-height: 1.6; }
        QLabel#noticeBar { background: #10221c; color: #cce8dd; border: 1px solid #376a58; border-radius: 9px; padding: 10px 12px; }
        QLabel#noticeBar[error="true"] { background: #2a1518; color: #ffc1c6; border-color: #754148; }
        QScrollArea { background: transparent; border: none; }
        QScrollArea > QWidget > QWidget { background: transparent; }
        QProgressBar { background: #101722; color: #dce5ef; border: 1px solid #2d384a; border-radius: 7px; text-align: center; min-height: 19px; }
        QProgressBar::chunk { background: #365f52; border-radius: 6px; }
        """


def install_unified_academy_dashboard(host: QWidget) -> UnifiedAcademyDashboard:
    """Replace only the native Academy page contents with the approved calm dashboard."""
    layout = host.layout()
    if layout is None:
        layout = QVBoxLayout(host)
    else:
        clear_layout(layout)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)
    dashboard = UnifiedAcademyDashboard(host)
    layout.addWidget(dashboard)
    host._shire_unified_academy_dashboard = dashboard
    return dashboard
