#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""CLI command: pulse proposals — Session-based idea review (S.I notation)."""
from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path

from pulse.core.paths import get_root_dir

ROOT = get_root_dir()
PROPOSALS_DIR = ROOT / "Prefrontal_Cortex" / "Working_Memory" / "Proposals"
TASK_BOARD = ROOT / "Prefrontal_Cortex" / "Action_Queue" / "task_board.json"
LEGACY_FILE = ROOT / "state" / "active_proposals.json"


def _load_sessions() -> list[dict]:
    """Load all session files, sorted newest first."""
    if not PROPOSALS_DIR.exists():
        return []
    sessions = []
    for f in sorted(PROPOSALS_DIR.glob("session_*.json"), reverse=True):
        try:
            data = json.loads(f.read_text(encoding="utf-8"))
            data["_filepath"] = str(f)
            sessions.append(data)
        except (json.JSONDecodeError, OSError):
            continue
    return sessions


def _save_session(session: dict):
    """Write a session back to disk."""
    filepath_str = session.get("_filepath", "")
    # Strip internal field before writing
    clean = {k: v for k, v in session.items() if k != "_filepath"}
    filepath = Path(filepath_str) if filepath_str else PROPOSALS_DIR / f"{session['id']}.json"
    filepath.write_text(
        json.dumps(clean, indent=2, ensure_ascii=False), encoding="utf-8"
    )
    session["_filepath"] = str(filepath)


def _load_task_board() -> dict:
    if not TASK_BOARD.exists():
        return {"dispatches": []}
    try:
        return json.loads(TASK_BOARD.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return {"dispatches": []}

def _save_task_board(board: dict):
    TASK_BOARD.write_text(json.dumps(board, indent=2, ensure_ascii=False), encoding="utf-8")


def _load_legacy() -> list[dict]:
    """Load legacy single-file proposals (governance votes)."""
    if not LEGACY_FILE.exists():
        return []
    try:
        data = json.loads(LEGACY_FILE.read_text(encoding="utf-8"))
        if isinstance(data, list):
            return data
        if isinstance(data, dict):
            if any(isinstance(v, dict) for v in data.values()):
                return list(data.values())
            return [data]
        return []
    except (json.JSONDecodeError, OSError):
        return []


def _parse_ref(ref: str) -> tuple[int, int | None]:
    """Parse 'S' or 'S.I' reference into (session_idx, idea_idx)."""
    if "." in ref:
        parts = ref.split(".", 1)
        return int(parts[0]), int(parts[1])
    return int(ref), None


def cmd_proposals(args: list):
    """Main entry: pulse proposals [list|view S[.I]|promote S.I|reject S.I|archive S|clear]."""
    if not args:
        return _list_sessions()
    sub = args[0].lower()
    if sub in ("list", "ls"):
        return _list_sessions()
    elif sub == "view":
        if len(args) > 1:
            return _view(args[1])
        return _list_sessions()
    elif sub == "promote" and len(args) > 1:
        return _promote(args[1])
    elif sub == "reject" and len(args) > 1:
        return _reject(args[1])
    elif sub == "archive" and len(args) > 1:
        return _archive(args[1])
    elif sub == "clear":
        return _clear_empty()
    else:
        _print_usage()


def _print_usage():
    print("Usage: pulse proposals [list|view S[.I]|promote S.I|reject S.I|archive S|clear]")


def _list_sessions():
    """List all sessions with inline idea summaries."""
    sessions = _load_sessions()
    legacy = _load_legacy()

    if not sessions and not legacy:
        print("\n  [DMN] No proposals. Run `pulse dream` to generate ideas.\n")
        return

    if sessions:
        total = sum(len(s.get("ideas", [])) for s in sessions)
        pending = sum(1 for s in sessions for x in s.get("ideas", []) if x.get("status") == "pending")
        print(f"\n  DMN Sessions: {len(sessions)} | Ideas: {total} ({pending} pending)\n")
        for i, s in enumerate(sessions):
            ideas = s.get("ideas", [])
            counts: dict[str, int] = {}
            for x in ideas:
                st = x.get("status", "pending")
                counts[st] = counts.get(st, 0) + 1
            status_str = ", ".join(f"{n} {k}" for k, n in counts.items()) or "empty"
            date = s.get("created", "")[:16].replace("T", " ")
            print(f"  [{i}] {date} | {s.get('mode', '?')} | {s.get('panel_size', len(ideas))} models | {status_str}")
            for j, idea in enumerate(ideas):
                icon = {"pending": " ", "promoted": "+", "rejected": "x"}.get(idea.get("status", "pending"), " ")
                title = (idea.get("title") or "Untitled")[:55]
                model = (idea.get("model") or "?").split(":")[0]
                print(f"      {icon} {i}.{j} {title}  ({model})")
        print()

    if legacy:
        print(f"  Legacy proposals: {len(legacy)} (in state/active_proposals.json)")
        print(f"  Use `pulse proposals view legacy` to see them.\n")


def _view(ref: str):
    """View a session or a specific idea."""
    if ref == "legacy":
        return _view_legacy()

    sessions = _load_sessions()
    try:
        s_idx, i_idx = _parse_ref(ref)
    except (ValueError, IndexError):
        _print_usage()
        return

    if s_idx < 0 or s_idx >= len(sessions):
        print(f"  Invalid session index {s_idx}. Range: 0-{len(sessions) - 1}")
        return

    session = sessions[s_idx]

    if i_idx is None:
        # Show full session overview
        print(f"\n  Session [{s_idx}]: {session.get('id', '?')}")
        print(f"  Created: {session.get('created', '?')} | Mode: {session.get('mode', '?')} | Files: {session.get('file_count', '?')}")
        print(f"  Source: {', '.join(session.get('source_files', []))}\n")
        for j, idea in enumerate(session.get("ideas", [])):
            icon = {"pending": " ", "promoted": "+", "rejected": "x"}.get(idea.get("status", "pending"), " ")
            print(f"  {icon} [{s_idx}.{j}] {idea.get('title', 'Untitled')}  ({idea.get('model', '?')})")
            print(f"    {idea.get('idea', '')[:200]}\n")
    else:
        ideas = session.get("ideas", [])
        if i_idx < 0 or i_idx >= len(ideas):
            print(f"  Invalid idea index {i_idx}. Range: 0-{len(ideas) - 1}")
            return
        idea = ideas[i_idx]
        merged = idea.get("modules_merged", session.get("source_files", []))
        print(f"\n  Idea [{s_idx}.{i_idx}]: {idea.get('title', 'Untitled')}")
        print(f"  Status: {idea.get('status', 'pending')} | Model: {idea.get('model', '?')} ({idea.get('provider', '?')})")
        print(f"  Session: {session.get('id', '?')} ({session.get('mode', '?')} mode)")
        print(f"  Source files: {', '.join(merged)}")
        print(f"\n  {idea.get('idea', 'No idea text.')}")
        if idea.get("impact"):
            print(f"\n  Impact: {idea['impact']}")
        print()


def _view_legacy():
    """View legacy proposals from state/active_proposals.json."""
    proposals = _load_legacy()
    if not proposals:
        return print("  No legacy proposals.")
    print(f"\n  Legacy Proposals ({len(proposals)}):\n")
    for i, p in enumerate(proposals):
        title = p.get("title") or p.get("action") or "Untitled"
        print(f"  [{i}] {title} | {p.get('model') or p.get('proposer_id') or '?'} | {p.get('status', 'pending')}")
    print()


def _promote(ref: str):
    """Promote an idea to the task board."""
    sessions = _load_sessions()
    try:
        s_idx, i_idx = _parse_ref(ref)
    except (ValueError, IndexError):
        _print_usage()
        return
    if i_idx is None:
        print("  Specify an idea: pulse proposals promote S.I (e.g., 0.2)")
        return
    if s_idx < 0 or s_idx >= len(sessions):
        print(f"  Invalid session index {s_idx}.")
        return
    session = sessions[s_idx]
    ideas = session.get("ideas", [])
    if i_idx < 0 or i_idx >= len(ideas):
        print(f"  Invalid idea index {i_idx}.")
        return
    idea = ideas[i_idx]
    if idea.get("status") == "promoted":
        print(f"  Idea {ref} already promoted.")
        return
    board = _load_task_board()
    mode = session.get("mode", "dmn")
    if "skill_synthesis" in mode:
        prefix, source = "d10", "skill_synthesizer"
    elif "epistemic" in mode:
        prefix, source = "d9", "epistemic_forager"
    else:
        prefix, source = "dmn", "dmn_daemon"
    task = {
        "id": f"{prefix}-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}",
        "title": idea.get("title", "DMN Proposal"),
        "description": idea.get("idea", ""),
        "domain": "architecture",
        "complexity": "premium",
        "recommended_tier": "premium",
        "status": "open",
        "source": source,
        "source_session": session.get("id", ""),
        "source_model": idea.get("model", ""),
        "created": datetime.now(timezone.utc).isoformat(),
    }
    board["dispatches"].append(task)
    _save_task_board(board)

    # Mark promoted
    idea["status"] = "promoted"
    idea["promoted_at"] = datetime.now(timezone.utc).isoformat()
    _save_session(session)

    print(f"  Promoted idea {ref} to task board as '{task['id']}'.")


def _reject(ref: str):
    """Reject an idea."""
    sessions = _load_sessions()
    try:
        s_idx, i_idx = _parse_ref(ref)
    except (ValueError, IndexError):
        return _print_usage()
    if i_idx is None:
        return print("  Specify an idea: pulse proposals reject S.I (e.g., 0.2)")
    if s_idx < 0 or s_idx >= len(sessions):
        return print(f"  Invalid session index {s_idx}.")
    ideas = sessions[s_idx].get("ideas", [])
    if i_idx < 0 or i_idx >= len(ideas):
        return print(f"  Invalid idea index {i_idx}.")
    ideas[i_idx]["status"] = "rejected"
    ideas[i_idx]["rejected_at"] = datetime.now(timezone.utc).isoformat()
    _save_session(sessions[s_idx])
    print(f"  Rejected idea {ref}.")


def _clear_empty():
    """Delete session files where all ideas are rejected."""
    removed = 0
    for s in _load_sessions():
        ideas = s.get("ideas", [])
        if ideas and all(x.get("status") == "rejected" for x in ideas):
            Path(s["_filepath"]).unlink(missing_ok=True)
            removed += 1
    print(f"  Cleared {removed} fully-rejected session(s).")


def _archive(ref: str):
    """Move a session to Proposals/Archive/ (subconscious backlog)."""
    sessions = _load_sessions()
    try:
        s_idx = int(ref.split(".")[0])
    except ValueError:
        return print("  Usage: pulse proposals archive S")
    if s_idx < 0 or s_idx >= len(sessions):
        return print(f"  Invalid session index {s_idx}.")
    src = Path(sessions[s_idx]["_filepath"])
    archive = PROPOSALS_DIR / "Archive"
    archive.mkdir(parents=True, exist_ok=True)
    src.rename(archive / src.name)
    print(f"  Archived session {s_idx} -> Archive/{src.name}")
