"""
Pradhya · The Agents Workshop · Unit 10
========================================

A minimal MCP server exposing three useful tools to any Claude app:
list_customers, now, search_notes. The protocol is identical whether
this is called from Claude Code, Claude Desktop, or your own agent.

Install:
    pip install "mcp[cli]"

Run:
    python tools_mcp.py

Wire up in Claude Desktop / Claude Code via:
    {
      "mcpServers": {
        "ops-tools": {
          "command": "python",
          "args": ["/absolute/path/to/tools_mcp.py"]
        }
      }
    }
"""

from __future__ import annotations

import datetime
import pathlib
import re
import sqlite3
from typing import Iterable

try:
    from mcp.server.fastmcp import FastMCP
except ImportError as e:
    raise SystemExit(
        "mcp not installed. Run: pip install 'mcp[cli]'"
    ) from e


# ---------------------------------------------------------------------
# Setup: a tiny SQLite DB so the demo runs offline.
# In a real server, replace with your real data layer.
# ---------------------------------------------------------------------
DB_PATH = pathlib.Path(__file__).parent / "ops.db"


def _ensure_db() -> None:
    if DB_PATH.exists():
        return
    con = sqlite3.connect(DB_PATH)
    cur = con.cursor()
    cur.executescript(
        """
        CREATE TABLE customers (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            plan TEXT NOT NULL,
            mrr REAL NOT NULL,
            updated_at TEXT NOT NULL
        );

        INSERT INTO customers (name, plan, mrr, updated_at) VALUES
            ('Acme Corp',       'pro',        249.00, '2026-05-01'),
            ('Bramble Studios', 'enterprise', 1499.00, '2026-05-12'),
            ('Cobalt Inc',      'starter',     49.00, '2026-04-22'),
            ('Driftwood LLC',   'pro',        249.00, '2026-05-17');
        """
    )
    con.commit()
    con.close()


_ensure_db()


# ---------------------------------------------------------------------
# The server
# ---------------------------------------------------------------------
mcp = FastMCP("ops-tools")


@mcp.tool()
def list_customers(modified_after: str | None = None, plan: str | None = None) -> list[dict]:
    """List customers from our SQLite store.

    Args:
        modified_after: ISO date (YYYY-MM-DD). If set, only customers updated since.
        plan: Filter to a specific plan tier ("starter" | "pro" | "enterprise").

    Returns: list of {id, name, plan, mrr, updated_at}.
    """
    con = sqlite3.connect(DB_PATH)
    sql = "SELECT id, name, plan, mrr, updated_at FROM customers WHERE 1=1"
    params: list = []
    if modified_after:
        sql += " AND updated_at > ?"
        params.append(modified_after)
    if plan:
        sql += " AND plan = ?"
        params.append(plan)
    sql += " ORDER BY mrr DESC"
    rows = con.execute(sql, params).fetchall()
    con.close()
    return [
        {"id": r[0], "name": r[1], "plan": r[2], "mrr": r[3], "updated_at": r[4]}
        for r in rows
    ]


@mcp.tool()
def now() -> str:
    """Return the current local datetime, ISO-formatted (seconds precision)."""
    return datetime.datetime.now().isoformat(timespec="seconds")


@mcp.tool()
def search_notes(query: str, root: str = "~/Documents", limit: int = 10) -> list[dict]:
    """Plain-text search across the user's notes folder.

    A starting point for personal RAG. Searches markdown files only.
    Args:
        query: case-insensitive substring to match.
        root: folder to search from (defaults to ~/Documents).
        limit: max number of hits to return.
    """
    base = pathlib.Path(root).expanduser()
    if not base.exists():
        return [{"error": f"path not found: {base}"}]

    needle = query.lower()
    hits: list[dict] = []
    for md in base.rglob("*.md"):
        try:
            text = md.read_text(encoding="utf-8", errors="replace")
        except OSError:
            continue
        idx = text.lower().find(needle)
        if idx < 0:
            continue
        excerpt = _excerpt(text, idx, span=120)
        hits.append({"path": str(md.relative_to(base)), "excerpt": excerpt})
        if len(hits) >= limit:
            break
    return hits


def _excerpt(text: str, idx: int, span: int = 120) -> str:
    start = max(0, idx - span)
    end   = min(len(text), idx + span)
    snippet = text[start:end].replace("\n", " ")
    snippet = re.sub(r"\s+", " ", snippet).strip()
    prefix  = "…" if start > 0 else ""
    suffix  = "…" if end < len(text) else ""
    return f"{prefix}{snippet}{suffix}"


if __name__ == "__main__":
    mcp.run()
