"""Native macOS overlay window — always-on-top, frameless, dark panel.

Launched as a separate process by the daemon.
Connects to the daemon WebSocket and renders live transcript + AI responses.

Usage (internal):  python -m meeting_helper.overlay <port>
"""

from __future__ import annotations

import json
import sys

from PySide6.QtCore import (
    QObject,
    QPoint,
    Qt,
    QThread,
    QTimer,
    Signal,
    Slot,
)
from PySide6.QtGui import QFont
from PySide6.QtWebSockets import QWebSocket
from PySide6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QScrollArea,
    QSizeGrip,
    QTextBrowser,
    QVBoxLayout,
    QWidget,
)

# ---------------------------------------------------------------------------
# Markdown -> HTML renderer
# ---------------------------------------------------------------------------

def _md_to_html(text: str, font_size: int = 14) -> str:
    """Convert markdown to styled HTML for QTextBrowser."""
    try:
        import markdown
        from pygments.formatters import HtmlFormatter

        formatter = HtmlFormatter(style="github-dark", noclasses=True)
        pygments_css = formatter.get_style_defs(".codehilite")

        extensions = ["fenced_code", "codehilite", "tables", "nl2br"]
        body = markdown.markdown(text, extensions=extensions)

        code_size = max(8, font_size - 2)
        return f"""<style>
body {{ color: #e6edf3; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
        font-size: {font_size}px; line-height: 1.65; margin: 0; padding: 0; background: transparent; }}
h1, h2, h3 {{ color: #ffffff; margin: 14px 0 6px; }}
h1 {{ font-size: {font_size + 2}px; }}
h2 {{ font-size: {font_size + 1}px; }}
h3 {{ font-size: {font_size}px; }}
p {{ margin: 6px 0; }}
code {{ background: #1c2128; border: 1px solid #30363d; border-radius: 3px;
        padding: 1px 5px; font-family: Menlo, monospace; font-size: {code_size}px; color: #79c0ff; }}
pre {{ background: #1c2128; border: 1px solid #30363d; border-radius: 6px;
       padding: 12px; margin: 8px 0; overflow-x: auto; }}
pre code {{ background: none; border: none; padding: 0; color: #e6edf3; font-size: {code_size}px; }}
ul, ol {{ padding-left: 18px; margin: 6px 0; }}
li {{ margin: 3px 0; }}
strong {{ color: #ffffff; }}
em {{ color: #d2a8ff; }}
blockquote {{ border-left: 3px solid #30363d; margin: 6px 0; padding-left: 10px; color: #8b949e; }}
table {{ border-collapse: collapse; width: 100%; margin: 8px 0; font-size: {font_size - 1}px; }}
th, td {{ border: 1px solid #30363d; padding: 5px 10px; }}
th {{ background: #1c2128; }}
{pygments_css}
</style>
{body}"""
    except Exception:
        escaped = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
        return f'<pre style="color:#e6edf3;white-space:pre-wrap">{escaped}</pre>'


# ---------------------------------------------------------------------------
# WebSocket worker
# ---------------------------------------------------------------------------

class WSWorker(QObject):
    message = Signal(dict)
    connected = Signal()
    disconnected = Signal()

    def __init__(self, port: int) -> None:
        super().__init__()
        self._port = port
        self._ws: QWebSocket | None = None

    def start(self) -> None:
        self._ws = QWebSocket()
        self._ws.connected.connect(self._on_connected)
        self._ws.disconnected.connect(self._on_disconnected)
        self._ws.textMessageReceived.connect(self._on_message)
        self._connect()

    def _connect(self) -> None:
        from PySide6.QtCore import QUrl
        self._ws.open(QUrl(f"ws://127.0.0.1:{self._port}/ws"))

    @Slot()
    def _on_connected(self) -> None:
        self.connected.emit()

    @Slot()
    def _on_disconnected(self) -> None:
        self.disconnected.emit()
        QTimer.singleShot(2000, self._connect)

    @Slot(str)
    def _on_message(self, raw: str) -> None:
        try:
            self.message.emit(json.loads(raw))
        except Exception:
            pass


# ---------------------------------------------------------------------------
# Overlay window
# ---------------------------------------------------------------------------

BG_COLOR = "#0d1117"
BG_ALPHA = 230

class OverlayWindow(QWidget):
    def __init__(self, port: int) -> None:
        super().__init__()
        self._port = port
        self._raw_md = ""
        self._current_html = ""
        self._history: list[dict] = []
        self._nav_index = -1
        self._max_history = 3
        self._font_size = 14
        self._drag_pos: QPoint | None = None
        self._thinking = False
        self._meeting_name = ""
        self._setup_window()
        self._build_ui()
        self._start_ws()

    def _setup_window(self) -> None:
        self.setWindowFlags(
            Qt.FramelessWindowHint
            | Qt.WindowStaysOnTopHint
        )
        self.setAttribute(Qt.WA_TranslucentBackground)

        screen = QApplication.primaryScreen().availableGeometry()
        width = min(screen.width() - 32, 820)
        height = 300
        self.resize(width, height)
        self.move(
            screen.left() + (screen.width() - width) // 2,
            screen.bottom() - height - 16,
        )

    def showEvent(self, event) -> None:
        """Hide window from screen capture/screen share."""
        super().showEvent(event)
        try:
            from AppKit import NSApp
            for window in NSApp.windows():
                window.setSharingType_(0)
        except Exception:
            pass

    def _build_ui(self) -> None:
        outer = QVBoxLayout(self)
        outer.setContentsMargins(0, 0, 0, 0)
        outer.setSpacing(0)

        card = QWidget()
        card.setObjectName("card")
        card.setStyleSheet(f"""
            QWidget#card {{
                background: rgba(13,17,23,{BG_ALPHA});
                border: 1px solid #30363d;
                border-radius: 12px;
            }}
        """)
        outer.addWidget(card)

        layout = QVBoxLayout(card)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Title bar
        title_bar = QWidget()
        title_bar.setFixedHeight(32)
        title_bar.setStyleSheet("background: transparent;")
        tb_layout = QHBoxLayout(title_bar)
        tb_layout.setContentsMargins(12, 0, 8, 0)

        self._status_dot = QLabel()
        self._status_dot.setText("\u25cf")
        self._status_dot.setStyleSheet("color: #555; font-size: 10px;")
        tb_layout.addWidget(self._status_dot)

        self._title_label = QLabel("Meeting Helper")
        self._title_label.setStyleSheet("color: #8b949e; font-size: 11px;")
        tb_layout.addWidget(self._title_label)
        tb_layout.addStretch()

        close_btn = QPushButton("\u2715")
        close_btn.setFixedSize(20, 20)
        close_btn.setCursor(Qt.PointingHandCursor)
        close_btn.setStyleSheet("""
            QPushButton { background: transparent; color: #555; border: none; font-size: 12px; }
            QPushButton:hover { color: #f85149; }
        """)
        close_btn.clicked.connect(self.close)
        tb_layout.addWidget(close_btn)

        layout.addWidget(title_bar)

        # Divider
        divider = QWidget()
        divider.setFixedHeight(1)
        divider.setStyleSheet("background: #21262d;")
        layout.addWidget(divider)

        # Thinking label
        self._thinking_label = QLabel("  Thinking...")
        self._thinking_label.setStyleSheet("color: #8b949e; font-size: 12px; padding: 8px 12px;")
        self._thinking_label.hide()
        layout.addWidget(self._thinking_label)

        # Scroll area + text browser for AI response
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(scroll.Shape.NoFrame)
        scroll.setStyleSheet("""
            QScrollArea { background: transparent; border: none; }
            QScrollBar:vertical { width: 5px; background: transparent; }
            QScrollBar::handle:vertical { background: #30363d; border-radius: 2px; }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
        """)

        self._browser = QTextBrowser()
        self._browser.setOpenExternalLinks(True)
        self._browser.setStyleSheet("background: transparent; border: none; color: #e6edf3;")
        self._browser.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        font = QFont("Helvetica Neue", self._font_size)
        self._browser.setFont(font)
        scroll.setWidget(self._browser)
        layout.addWidget(scroll)

        self._scroll = scroll

        # Live transcript label at bottom
        self._transcript_label = QLabel()
        self._transcript_label.setWordWrap(True)
        self._transcript_label.setStyleSheet(
            "color: #8b949e; font-size: 11px; padding: 4px 12px 2px; background: transparent;"
        )
        self._transcript_label.hide()
        layout.addWidget(self._transcript_label)

        # Manual question input
        input_row = QWidget()
        input_row.setStyleSheet("background: transparent;")
        ir_layout = QHBoxLayout(input_row)
        ir_layout.setContentsMargins(8, 4, 8, 4)
        ir_layout.setSpacing(6)

        self._input = QLineEdit()
        self._input.setPlaceholderText("Ask Claude something... (Enter to send)")
        self._input.setStyleSheet("""
            QLineEdit {
                background: #161b22; border: 1px solid #30363d; border-radius: 6px;
                color: #e6edf3; padding: 5px 10px; font-size: 12px;
            }
            QLineEdit:focus { border-color: #58a6ff; }
        """)
        self._input.returnPressed.connect(self._send_manual_query)
        ir_layout.addWidget(self._input)

        send_btn = QPushButton("\u27a4")
        send_btn.setFixedSize(28, 28)
        send_btn.setCursor(Qt.PointingHandCursor)
        send_btn.setStyleSheet("""
            QPushButton { background: #238636; color: white; border: none; border-radius: 6px; font-size: 14px; }
            QPushButton:hover { background: #2ea043; }
        """)
        send_btn.clicked.connect(self._send_manual_query)
        ir_layout.addWidget(send_btn)

        layout.addWidget(input_row)

        # Resize grip
        grip_row = QWidget()
        grip_row.setStyleSheet("background: transparent;")
        grip_layout = QHBoxLayout(grip_row)
        grip_layout.setContentsMargins(0, 0, 4, 4)
        grip_layout.addStretch()
        grip = QSizeGrip(self)
        grip.setStyleSheet("background: transparent;")
        grip_layout.addWidget(grip)
        layout.addWidget(grip_row)

    def _start_ws(self) -> None:
        self._ws_thread = QThread()
        self._ws_worker = WSWorker(self._port)
        self._ws_worker.moveToThread(self._ws_thread)
        self._ws_thread.started.connect(self._ws_worker.start)
        self._ws_worker.message.connect(self._on_message)
        self._ws_worker.connected.connect(self._on_connected)
        self._ws_worker.disconnected.connect(self._on_disconnected)
        self._ws_thread.start()

    # ---- Manual query ----

    def _send_manual_query(self) -> None:
        text = self._input.text().strip()
        if not text:
            return
        self._input.clear()
        import threading
        threading.Thread(target=self._post_query, args=(text,), daemon=True).start()

    def _post_query(self, text: str) -> None:
        """Send query via HTTP POST in background thread."""
        import urllib.request
        try:
            body = json.dumps({"text": text}).encode()
            req = urllib.request.Request(
                f"http://127.0.0.1:{self._port}/query",
                data=body,
                headers={"Content-Type": "application/json"},
            )
            urllib.request.urlopen(req, timeout=60)
        except Exception:
            pass  # errors will be shown via WebSocket broadcast

    # ---- Message handling ----

    @Slot(dict)
    def _on_message(self, msg: dict) -> None:
        t = msg.get("type")

        if t == "status":
            val = msg.get("value", "idle")
            if val == "processing":
                self._status_dot.setStyleSheet("color: #f85149; font-size: 10px;")
            elif val == "listening":
                self._status_dot.setStyleSheet("color: #3fb950; font-size: 10px;")
            else:
                self._status_dot.setStyleSheet("color: #555; font-size: 10px;")

        elif t == "meeting_status":
            if msg.get("active"):
                app_name = msg.get("app", "")
                meeting_name = msg.get("meeting_name", "")
                parts = ["Meeting Helper"]
                if app_name:
                    parts.append(app_name)
                if meeting_name:
                    parts.append(f'"{meeting_name}"')
                self._meeting_name = meeting_name or app_name
                self._title_label.setText("  \u00b7  ".join(parts))
                self._title_label.setStyleSheet("color: #3fb950; font-size: 11px;")
            else:
                self._meeting_name = ""
                self._title_label.setText("Meeting Helper  \u00b7  idle")
                self._title_label.setStyleSheet("color: #8b949e; font-size: 11px;")

        elif t == "meeting_ended":
            # Clear everything from the previous call
            self._raw_md = ""
            self._current_html = ""
            self._history.clear()
            self._nav_index = -1
            self._meeting_name = ""
            self._browser.setHtml("")
            self._thinking_label.hide()
            self._transcript_label.setText("")
            self._transcript_label.hide()
            self._title_label.setText("Meeting Helper  \u00b7  idle")
            self._title_label.setStyleSheet("color: #8b949e; font-size: 11px;")
            self._status_dot.setStyleSheet("color: #555; font-size: 10px;")

        elif t == "speech_started":
            self._transcript_label.show()

        elif t == "transcript":
            text = msg.get("text", "")
            if msg.get("interim"):
                display = "\U0001f3a4 " + (text[-110:] if len(text) > 110 else text)
                self._transcript_label.setStyleSheet(
                    "color: #8b949e; font-size: 11px; padding: 4px 12px 2px; background: transparent;"
                )
            else:
                current = self._transcript_label.text().lstrip("\U0001f3a4 ")
                combined = (current + " " + text).strip()
                if len(combined) > 110:
                    combined = "\u2026" + combined[-107:]
                display = "\U0001f3a4 " + combined
                self._transcript_label.setStyleSheet(
                    "color: #3fb950; font-size: 11px; padding: 4px 12px 2px; background: transparent;"
                )
                QTimer.singleShot(800, lambda: self._transcript_label.setStyleSheet(
                    "color: #8b949e; font-size: 11px; padding: 4px 12px 2px; background: transparent;"
                ))
            self._transcript_label.setText(display)
            self._transcript_label.show()

        elif t == "start":
            if self._raw_md:
                self._history.insert(0, {"md": self._raw_md, "html": self._current_html})
                if len(self._history) > self._max_history:
                    self._history.pop()
            self._raw_md = ""
            self._current_html = ""
            self._nav_index = -1
            self._browser.setHtml("")
            self._transcript_label.hide()
            self._transcript_label.setText("")
            self._thinking_label.setText("  Thinking...")
            self._thinking_label.show()

        elif t == "chunk":
            self._thinking_label.hide()
            self._raw_md += msg.get("text", "")
            sb = self._scroll.verticalScrollBar()
            was_at_bottom = sb.maximum() == 0 or (sb.maximum() - sb.value()) < 60
            old_pos = sb.value()
            self._browser.setHtml(_md_to_html(self._raw_md, self._font_size))
            self._current_html = self._raw_md
            if was_at_bottom:
                QTimer.singleShot(0, lambda: self._scroll.verticalScrollBar().setValue(
                    self._scroll.verticalScrollBar().maximum()
                ))
            else:
                QTimer.singleShot(0, lambda p=old_pos: self._scroll.verticalScrollBar().setValue(p))

        elif t == "done":
            self._thinking_label.hide()
            full = msg.get("full_text", "")
            if full and not self._raw_md:
                self._raw_md = full
                self._browser.setHtml(_md_to_html(self._raw_md, self._font_size))
                self._current_html = self._raw_md

        elif t == "error":
            self._thinking_label.hide()
            err_html = f'<p style="color:#f85149">Warning: {msg.get("message", "Unknown error")}</p>'
            self._browser.setHtml(err_html)

        elif t == "scroll":
            sb = self._browser.verticalScrollBar()
            delta = 150 if msg.get("direction") == "down" else -150
            sb.setValue(sb.value() + delta)

        elif t == "nav":
            total = 1 + len(self._history)
            if total < 2:
                return
            if msg.get("direction") == "prev":
                self._nav_index = min(self._nav_index + 1, len(self._history) - 1)
            else:
                self._nav_index = max(self._nav_index - 1, -1)
            md = self._raw_md if self._nav_index == -1 else self._history[self._nav_index].get("md", "")
            self._browser.setHtml(_md_to_html(md, self._font_size))
            label = "current" if self._nav_index == -1 else f"{self._nav_index + 1} of {len(self._history)} ago"
            self._thinking_label.setText(f"  {label}")
            self._thinking_label.show()
            QTimer.singleShot(1500, lambda: self._thinking_label.hide() if not self._thinking else None)

        elif t == "overlay_toggle":
            if msg.get("visible"):
                self.show()
            else:
                self.hide()

        elif t == "font_size":
            self._font_size += 1 if msg.get("direction") == "up" else -1
            self._font_size = max(8, min(32, self._font_size))
            font = self._browser.font()
            font.setPointSize(self._font_size)
            self._browser.setFont(font)
            md = self._raw_md if self._nav_index == -1 else self._history[self._nav_index].get("md", "")
            if md:
                self._browser.setHtml(_md_to_html(md, self._font_size))

    @Slot()
    def _on_connected(self) -> None:
        title = "Meeting Helper  \u00b7  connected"
        if self._meeting_name:
            title = f"Meeting Helper  \u00b7  {self._meeting_name}"
        self._title_label.setText(title)
        self._title_label.setStyleSheet("color: #3fb950; font-size: 11px;")

    @Slot()
    def _on_disconnected(self) -> None:
        self._title_label.setText("Meeting Helper  \u00b7  reconnecting...")
        self._title_label.setStyleSheet("color: #d29922; font-size: 11px;")

    # ---- Cleanup ----

    def closeEvent(self, event) -> None:
        if hasattr(self, "_ws_thread") and self._ws_thread.isRunning():
            self._ws_thread.quit()
            self._ws_thread.wait(2000)
        super().closeEvent(event)

    # ---- Drag to move ----

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()

    def mouseMoveEvent(self, event):
        if self._drag_pos and event.buttons() & Qt.LeftButton:
            self.move(event.globalPosition().toPoint() - self._drag_pos)

    def mouseReleaseEvent(self, event):
        self._drag_pos = None


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main() -> None:
    import os
    try:
        os.setsid()
    except OSError:
        pass

    port = int(sys.argv[1]) if len(sys.argv) > 1 else 7891

    app = QApplication(sys.argv)
    app.setApplicationName("Meeting Helper")

    # Hide from Dock on macOS
    try:
        from AppKit import NSApplication, NSApplicationActivationPolicyAccessory
        NSApplication.sharedApplication().setActivationPolicy_(
            NSApplicationActivationPolicyAccessory
        )
    except Exception:
        pass

    window = OverlayWindow(port)
    window.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()
