"""
Dashboard API - Notes, Links/Resources, Changelog.
All stored in Postgres (dashboard_items table) using the checkpoint DB.
"""
import json
import logging
import uuid
from datetime import datetime, timedelta, timezone
from typing import Any, Optional

import psycopg
from psycopg import sql
from psycopg_pool import AsyncConnectionPool
from fastapi import APIRouter, Header, HTTPException
from pydantic import BaseModel

from config import CHECKPOINT_POSTGRES_URL

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/dashboard", tags=["Dashboard"])

TABLE = "dashboard_items"
VN_TZ = timezone(timedelta(hours=7))


# ═══════════════════════════════════════════════════
#  DB POOL (reuse pattern from conversation_manager)
# ═══════════════════════════════════════════════════

_pool: AsyncConnectionPool | None = None


async def _get_pool() -> AsyncConnectionPool:
    global _pool
    if _pool is None:
        _pool = AsyncConnectionPool(
            CHECKPOINT_POSTGRES_URL,
            min_size=1,
            max_size=5,
            max_lifetime=600,
            max_idle=300,
            open=False,
        )
        await _pool.open()
        await _init_table()
    return _pool


async def _init_table():
    """Create dashboard_items table if not exists."""
    pool = await _get_pool() if _pool else _pool
    if pool is None:
        return
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            await cur.execute(f"""
                CREATE TABLE IF NOT EXISTS {TABLE} (
                    id VARCHAR(36) PRIMARY KEY,
                    type VARCHAR(20) NOT NULL DEFAULT 'note',
                    title TEXT NOT NULL DEFAULT '',
                    content TEXT NOT NULL DEFAULT '',
                    url TEXT DEFAULT '',
                    description TEXT DEFAULT '',
                    category VARCHAR(50) DEFAULT 'note',
                    icon VARCHAR(10) DEFAULT '',
                    color VARCHAR(20) DEFAULT '',
                    pinned BOOLEAN DEFAULT FALSE,
                    author VARCHAR(100) DEFAULT 'Admin',
                    metadata JSONB DEFAULT '{{}}'::jsonb,
                    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
                )
            """)
            await cur.execute(f"""
                CREATE INDEX IF NOT EXISTS idx_dashboard_items_type 
                ON {TABLE} (type, pinned DESC, updated_at DESC)
            """)
        await conn.commit()
    logger.info(f"✅ Table {TABLE} initialized")


async def _query(query_str: str, params: tuple = ()) -> list[dict]:
    pool = await _get_pool()
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            await cur.execute(query_str, params)
            cols = [d[0] for d in cur.description] if cur.description else []
            rows = await cur.fetchall()
            return [dict(zip(cols, row)) for row in rows]


async def _execute(query_str: str, params: tuple = ()):
    pool = await _get_pool()
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            await cur.execute(query_str, params)
        await conn.commit()


def _now():
    return datetime.now(VN_TZ)


def _row_to_dict(row: dict) -> dict:
    """Convert DB row to API-friendly dict."""
    r = {**row}
    # Convert datetime to ISO string
    for k in ("created_at", "updated_at"):
        if k in r and r[k]:
            r[k] = r[k].isoformat()
    # Merge metadata into response if present
    meta = r.pop("metadata", None)
    if meta and isinstance(meta, dict):
        r.update(meta)
    return r


# ═══════════════════════════════════════════════════
#  Pydantic Models
# ═══════════════════════════════════════════════════

class NoteCreate(BaseModel):
    title: str
    content: str
    category: str = "note"
    pinned: bool = False
    color: Optional[str] = None


class NoteUpdate(BaseModel):
    title: Optional[str] = None
    content: Optional[str] = None
    category: Optional[str] = None
    pinned: Optional[bool] = None
    color: Optional[str] = None


class LinkCreate(BaseModel):
    title: str
    url: str
    description: str = ""
    category: str = "other"
    icon: str = ""
    pinned: bool = False


class LinkUpdate(BaseModel):
    title: Optional[str] = None
    url: Optional[str] = None
    description: Optional[str] = None
    category: Optional[str] = None
    icon: Optional[str] = None
    pinned: Optional[bool] = None


class ChangelogCreate(BaseModel):
    content: str
    author: str = "Admin"


class UserCreate(BaseModel):
    name: str
    role: str = "user"


# ═══════════════════════════════════════════════════
#  NOTES CRUD
# ═══════════════════════════════════════════════════

def _default_color(category: str) -> str:
    return {
        "note": "#58a6ff", "doc": "#a78bfa",
        "todo": "#e3b341", "announcement": "#f85149",
    }.get(category, "#58a6ff")


@router.get("/notes")
async def list_notes():
    """Get all notes, pinned first."""
    rows = await _query(
        f"SELECT * FROM {TABLE} WHERE type = 'note' ORDER BY pinned DESC, updated_at DESC"
    )
    notes = [_row_to_dict(r) for r in rows]
    return {"status": "success", "notes": notes, "total": len(notes)}


@router.post("/notes")
async def create_note(note: NoteCreate, x_author: str = Header(default="Admin")):
    """Create a new note."""
    now = _now()
    nid = str(uuid.uuid4())[:8]
    color = note.color or _default_color(note.category)
    await _execute(
        f"""INSERT INTO {TABLE} (id, type, title, content, category, color, pinned, author, created_at, updated_at)
            VALUES (%s, 'note', %s, %s, %s, %s, %s, %s, %s, %s)""",
        (nid, note.title, note.content, note.category, color, note.pinned, x_author, now, now),
    )
    return {"status": "success", "note": {
        "id": nid, "title": note.title, "content": note.content,
        "category": note.category, "color": color, "pinned": note.pinned,
        "author": x_author, "created_at": now.isoformat(), "updated_at": now.isoformat(),
    }}


@router.put("/notes/{note_id}")
async def update_note(note_id: str, update: NoteUpdate, x_author: str = Header(default="Admin")):
    """Update a note."""
    sets, vals = [], []
    for field in ("title", "content", "category", "pinned", "color"):
        val = getattr(update, field, None)
        if val is not None:
            sets.append(f"{field} = %s")
            vals.append(val)
    if not sets:
        raise HTTPException(status_code=400, detail="Nothing to update")
    sets.append("updated_at = %s")
    vals.append(_now())
    vals.append(note_id)
    await _execute(
        f"UPDATE {TABLE} SET {', '.join(sets)} WHERE id = %s AND type = 'note'", tuple(vals)
    )
    rows = await _query(f"SELECT * FROM {TABLE} WHERE id = %s", (note_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Note not found")
    return {"status": "success", "note": _row_to_dict(rows[0])}


@router.delete("/notes/{note_id}")
async def delete_note(note_id: str, x_author: str = Header(default="Admin")):
    """Delete a note."""
    rows = await _query(f"SELECT author FROM {TABLE} WHERE id = %s AND type = 'note'", (note_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Note not found")
    if rows[0]["author"] != x_author and x_author != "Admin":
        raise HTTPException(status_code=403, detail=f"Bạn không thể xóa note của {rows[0]['author']}")
    await _execute(f"DELETE FROM {TABLE} WHERE id = %s", (note_id,))
    return {"status": "success", "message": "Note deleted"}


@router.patch("/notes/{note_id}/pin")
async def toggle_pin(note_id: str):
    """Toggle pin status."""
    rows = await _query(f"SELECT pinned FROM {TABLE} WHERE id = %s AND type = 'note'", (note_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Note not found")
    new_pin = not rows[0]["pinned"]
    await _execute(
        f"UPDATE {TABLE} SET pinned = %s, updated_at = %s WHERE id = %s",
        (new_pin, _now(), note_id),
    )
    return {"status": "success", "pinned": new_pin}


# ═══════════════════════════════════════════════════
#  LINKS / RESOURCES CRUD
# ═══════════════════════════════════════════════════

DEFAULT_TOOL_LINKS = [
    {"id": "tool_stress_test", "title": "Stress Test Dashboard", "url": "/static/stress-test.html",
     "description": "Bắn N request đồng thời — đo RPS, latency p95, error rate", "category": "tool", "icon": "🔥"},
    {"id": "tool_regression_test", "title": "Regression Test", "url": "/static/regression-test.html",
     "description": "Chạy bộ test cases qua bất kỳ chatbot URL — con người đọc kết quả", "category": "tool", "icon": "🧪"},
    {"id": "tool_user_simulator", "title": "User Simulator", "url": "/static/user-simulator.html",
     "description": "AI đóng vai persona test chatbot — đánh giá từ góc user thật", "category": "tool", "icon": "🎭"},
    {"id": "tool_prompt_optimizer", "title": "Prompt Optimizer", "url": "/static/prompt-optimizer.html",
     "description": "AI Judge chấm điểm chatbot + tự động gợi ý cải thiện prompt", "category": "tool", "icon": "⚡"},
    {"id": "tool_competitor_research", "title": "Competitor Research", "url": "/static/competitor-research.html",
     "description": "Nghiên cứu đối thủ — phân tích Zalando, YaMe, H&M", "category": "doc", "icon": "🔍"},
]


def _default_icon(category: str) -> str:
    return {"tool": "🔧", "doc": "📄", "design": "🎨", "api": "🔌", "repo": "📦", "other": "🔗"}.get(category, "🔗")


async def _seed_default_links():
    """Auto-seed default tool links if missing."""
    rows = await _query(f"SELECT id FROM {TABLE} WHERE type = 'link'")
    existing_ids = {r["id"] for r in rows}
    now = _now()
    for link in DEFAULT_TOOL_LINKS:
        if link["id"] not in existing_ids:
            await _execute(
                f"""INSERT INTO {TABLE} (id, type, title, url, description, category, icon, pinned, author, created_at, updated_at)
                    VALUES (%s, 'link', %s, %s, %s, %s, %s, FALSE, 'System', %s, %s)""",
                (link["id"], link["title"], link["url"], link["description"], link["category"], link["icon"], now, now),
            )


@router.get("/links")
async def list_links():
    """Get all resource links, pinned first."""
    await _seed_default_links()
    rows = await _query(
        f"SELECT * FROM {TABLE} WHERE type = 'link' ORDER BY pinned DESC, created_at ASC"
    )
    links = [_row_to_dict(r) for r in rows]
    return {"status": "success", "links": links, "total": len(links)}


@router.post("/links")
async def create_link(link: LinkCreate, x_author: str = Header(default="Admin")):
    """Create a new resource link."""
    now = _now()
    lid = str(uuid.uuid4())[:8]
    icon = link.icon or _default_icon(link.category)
    await _execute(
        f"""INSERT INTO {TABLE} (id, type, title, url, description, category, icon, pinned, author, created_at, updated_at)
            VALUES (%s, 'link', %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
        (lid, link.title, link.url, link.description, link.category, icon, link.pinned, x_author, now, now),
    )
    return {"status": "success", "link": {
        "id": lid, "title": link.title, "url": link.url,
        "description": link.description, "category": link.category,
        "icon": icon, "pinned": link.pinned, "author": x_author,
        "created_at": now.isoformat(),
    }}


@router.put("/links/{link_id}")
async def update_link(link_id: str, update: LinkUpdate, x_author: str = Header(default="Admin")):
    """Update a link."""
    sets, vals = [], []
    for field in ("title", "url", "description", "category", "icon", "pinned"):
        val = getattr(update, field, None)
        if val is not None:
            sets.append(f"{field} = %s")
            vals.append(val)
    if not sets:
        raise HTTPException(status_code=400, detail="Nothing to update")
    vals.append(link_id)
    await _execute(
        f"UPDATE {TABLE} SET {', '.join(sets)} WHERE id = %s AND type = 'link'", tuple(vals)
    )
    rows = await _query(f"SELECT * FROM {TABLE} WHERE id = %s", (link_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")
    return {"status": "success", "link": _row_to_dict(rows[0])}


@router.delete("/links/{link_id}")
async def delete_link(link_id: str, x_author: str = Header(default="Admin")):
    """Delete a link."""
    rows = await _query(f"SELECT author FROM {TABLE} WHERE id = %s AND type = 'link'", (link_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")
    if rows[0]["author"] != x_author and x_author != "Admin":
        raise HTTPException(status_code=403, detail=f"Bạn không thể xóa link của {rows[0]['author']}")
    await _execute(f"DELETE FROM {TABLE} WHERE id = %s", (link_id,))
    return {"status": "success", "message": "Link deleted"}


@router.patch("/links/{link_id}/pin")
async def toggle_link_pin(link_id: str):
    """Toggle pin status."""
    rows = await _query(f"SELECT pinned FROM {TABLE} WHERE id = %s AND type = 'link'", (link_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")
    new_pin = not rows[0]["pinned"]
    await _execute(f"UPDATE {TABLE} SET pinned = %s WHERE id = %s", (new_pin, link_id))
    return {"status": "success", "pinned": new_pin}


# ═══════════════════════════════════════════════════
#  CHANGELOG CRUD
# ═══════════════════════════════════════════════════

@router.get("/changelog")
async def list_changelog():
    """Get all changelog entries, newest first."""
    rows = await _query(
        f"SELECT * FROM {TABLE} WHERE type = 'changelog' ORDER BY created_at DESC"
    )
    entries = [_row_to_dict(r) for r in rows]
    return {"status": "success", "entries": entries, "total": len(entries)}


@router.post("/changelog")
async def create_changelog_entry(entry: ChangelogCreate):
    """Add a new changelog entry."""
    now = _now()
    eid = str(uuid.uuid4())[:8]
    await _execute(
        f"""INSERT INTO {TABLE} (id, type, content, author, created_at, updated_at)
            VALUES (%s, 'changelog', %s, %s, %s, %s)""",
        (eid, entry.content, entry.author, now, now),
    )
    return {"status": "success", "entry": {
        "id": eid, "content": entry.content, "author": entry.author,
        "created_at": now.isoformat(),
    }}


@router.delete("/changelog/{entry_id}")
async def delete_changelog_entry(entry_id: str):
    """Delete a changelog entry."""
    rows = await _query(f"SELECT id FROM {TABLE} WHERE id = %s AND type = 'changelog'", (entry_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Entry not found")
    await _execute(f"DELETE FROM {TABLE} WHERE id = %s", (entry_id,))
    return {"status": "success", "message": "Entry deleted"}


# ═══════════════════════════════════════════════════
#  USERS / ROLES (still simple — kept in DB too)
# ═══════════════════════════════════════════════════

async def _init_users_table():
    """Users table separate for simplicity."""
    pool = await _get_pool()
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            await cur.execute("""
                CREATE TABLE IF NOT EXISTS dashboard_users (
                    name VARCHAR(100) PRIMARY KEY,
                    role VARCHAR(20) DEFAULT 'user'
                )
            """)
            # Seed admin
            await cur.execute("""
                INSERT INTO dashboard_users (name, role) VALUES ('Admin', 'admin')
                ON CONFLICT (name) DO NOTHING
            """)
        await conn.commit()


@router.get("/users")
async def list_users():
    """Get all users."""
    await _init_users_table()
    rows = await _query("SELECT * FROM dashboard_users ORDER BY name")
    return {"status": "success", "users": [dict(r) for r in rows]}


@router.post("/users")
async def add_user(user: UserCreate):
    """Add or update a user role."""
    await _init_users_table()
    await _execute(
        """INSERT INTO dashboard_users (name, role) VALUES (%s, %s)
           ON CONFLICT (name) DO UPDATE SET role = EXCLUDED.role""",
        (user.name, user.role),
    )
    rows = await _query("SELECT * FROM dashboard_users ORDER BY name")
    return {"status": "success", "message": f"Updated {user.name} to {user.role}", "users": [dict(r) for r in rows]}


@router.delete("/users/{name}")
async def remove_user(name: str):
    """Remove a user."""
    await _init_users_table()
    await _execute("DELETE FROM dashboard_users WHERE name = %s", (name,))
    rows = await _query("SELECT * FROM dashboard_users ORDER BY name")
    return {"status": "success", "users": [dict(r) for r in rows]}