import json
from urllib.parse import urlparse, parse_qs
from devmemory.db.memory_repo import MemoryRepo
from devmemory.db.state_repo import StateRepo
from devmemory.db.issue_repo import IssueRepo


def handle_api_request(handler, cm):
    parsed = urlparse(handler.path)
    path = parsed.path
    params = parse_qs(parsed.query)

    routes = {
        "GET": {
            "/api/memories": lambda: get_memories(cm, params),
            "/api/status": lambda: get_status(cm),
            "/api/issues": lambda: get_issues(cm, params),
            "/api/stats": lambda: get_stats(cm),
        },
        "PUT": {
            "/api/status": lambda: put_status(handler, cm),
        },
        "DELETE": {
            "/api/memories": lambda: delete_memories_batch(handler, cm),
        },
    }

    method = handler.command
    if path.startswith("/api/memories/") and len(path.split("/")) == 4:
        mid = path.split("/")[3]
        if method == "GET":
            return _json_response(handler, get_memory_detail(cm, mid))
        elif method == "PUT":
            return _json_response(handler, put_memory(handler, cm, mid))
        elif method == "DELETE":
            return _json_response(handler, delete_memory(cm, mid))

    if path.startswith("/api/issues/") and len(path.split("/")) == 4:
        iid = int(path.split("/")[3])
        if method == "PUT":
            return _json_response(handler, put_issue(handler, cm, iid))

    route_fn = routes.get(method, {}).get(path)
    if route_fn:
        _json_response(handler, route_fn())
    else:
        handler.send_error(404, "API not found")


def _read_body(handler) -> dict:
    length = int(handler.headers.get("Content-Length", 0))
    return json.loads(handler.rfile.read(length)) if length else {}


def _json_response(handler, data, status=200):
    body = json.dumps(data, ensure_ascii=False).encode("utf-8")
    handler.send_response(status)
    handler.send_header("Content-Type", "application/json; charset=utf-8")
    handler.send_header("Content-Length", len(body))
    handler.end_headers()
    handler.wfile.write(body)


def get_memories(cm, params):
    scope = params.get("scope", ["all"])[0]
    query = params.get("query", [None])[0]
    tag = params.get("tag", [None])[0]
    limit = int(params.get("limit", [100])[0])
    offset = int(params.get("offset", [0])[0])

    repo = MemoryRepo(cm.conn, cm.project_dir)
    pdir = cm.project_dir if scope == "project" else ("" if scope == "user" else None)

    if tag:
        all_rows = repo.list_by_tags([tag], scope=scope, project_dir=pdir or "", limit=9999)
        total = len(all_rows)
        results = all_rows[offset:offset + limit]
    else:
        rows = repo.get_all(limit=limit, offset=offset, project_dir=pdir)
        total = repo.count(project_dir=pdir)
        results = [r for r in rows if not query or query.lower() in r.get("content", "").lower()] if query else rows

    return {"memories": results, "total": total}


def get_memory_detail(cm, mid):
    repo = MemoryRepo(cm.conn, cm.project_dir)
    mem = repo.get_by_id(mid)
    return mem or {"error": "not found"}


def put_memory(handler, cm, mid):
    body = _read_body(handler)
    repo = MemoryRepo(cm.conn, cm.project_dir)
    mem = repo.get_by_id(mid)
    if not mem:
        return {"error": "not found"}
    now = repo._now()
    updates = {}
    if "content" in body:
        updates["content"] = body["content"]
    if "tags" in body:
        updates["tags"] = json.dumps(body["tags"])
    if updates:
        updates["updated_at"] = now
        set_clause = ",".join(f"{k}=?" for k in updates)
        cm.conn.execute(f"UPDATE memories SET {set_clause} WHERE id=?", [*updates.values(), mid])
        cm.conn.commit()
    return repo.get_by_id(mid)


def delete_memory(cm, mid):
    repo = MemoryRepo(cm.conn, cm.project_dir)
    if repo.delete(mid):
        return {"deleted": True, "id": mid}
    return {"error": "not found"}


def delete_memories_batch(handler, cm):
    body = _read_body(handler)
    ids = body.get("ids", [])
    repo = MemoryRepo(cm.conn, cm.project_dir)
    deleted = [mid for mid in ids if repo.delete(mid)]
    return {"deleted_count": len(deleted), "ids": deleted}


def get_status(cm):
    repo = StateRepo(cm.conn, cm.project_dir)
    state = repo.get()
    return state or {"empty": True}


def put_status(handler, cm):
    body = _read_body(handler)
    repo = StateRepo(cm.conn, cm.project_dir)
    return repo.upsert(**body)


def get_issues(cm, params):
    date = params.get("date", [None])[0]
    status = params.get("status", [None])[0]
    include_archived = params.get("include_archived", ["false"])[0] == "true"
    repo = IssueRepo(cm.conn, cm.project_dir)
    if status == "archived":
        issues = repo.list_archived(date=date)
    else:
        issues = repo.list_by_date(date=date, status=status)
        if include_archived:
            issues = issues + repo.list_archived(date=date)
    return {"issues": issues}


def put_issue(handler, cm, iid):
    body = _read_body(handler)
    repo = IssueRepo(cm.conn, cm.project_dir)
    result = repo.update(iid, **body)
    return result or {"error": "not found"}


def get_stats(cm):
    repo = MemoryRepo(cm.conn, cm.project_dir)
    issue_repo = IssueRepo(cm.conn, cm.project_dir)

    proj_count = repo.count(project_dir=cm.project_dir)
    user_count = repo.count(project_dir="")
    total_count = repo.count()

    all_issues = issue_repo.list_by_date()
    status_counts = {}
    for i in all_issues:
        s = i["status"]
        status_counts[s] = status_counts.get(s, 0) + 1

    tag_counts = {}
    for m in repo.get_all(limit=1000):
        mtags = json.loads(m.get("tags", "[]")) if isinstance(m.get("tags"), str) else m.get("tags", [])
        for t in mtags:
            tag_counts[t] = tag_counts.get(t, 0) + 1

    return {
        "memories": {"project": proj_count, "user": user_count, "total": total_count},
        "issues": status_counts,
        "tags": tag_counts,
    }
