#!/usr/bin/env python3
"""
List Confluence Cloud spaces (personal vs global) → data/confluence.json for the dashboard.

Uses GET /wiki/rest/api/space (paginated) with status=current. Each space includes type
("personal" | "global" in typical sites).

Optional: IT Operations space homepage title for the dashboard (see CONFLUENCE_IT_OPS_SPACE_KEY)
— GET space?expand=homepage then GET content for the page title; overview URL is always …/spaces/<key>/overview.

Auth: same as Jira — Basic (Atlassian account email + API token).

Setup:
  1. JIRA_EMAIL + JIRA_API_TOKEN in .env (same token works for Confluence on the same site).
  2. Optional: CONFLUENCE_WIKI_BASE (default: JIRA_BASE_URL + /wiki, e.g. https://ilabs.atlassian.net/wiki)
  3. Optional: CONFLUENCE_IT_OPS_SPACE_KEY (default IOT) for itOpsConfluenceHome in JSON.
  4. python3 app/src/jobs/fetch_confluence.py
"""
from __future__ import annotations

import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import quote

import requests

from store import write_json_snapshot

from env_config import ConfigError, confluence_wiki_base, env_int, env_str
from paths import DATA_DIR, REPO_ROOT, load_env_file

ROOT = REPO_ROOT
OUT = Path(os.environ.get("CONFLUENCE_OUT", str(DATA_DIR / "confluence.json")))
PAGE_LIMIT = 100


def _max_spaces() -> int:
    return env_int("CONFLUENCE_SPACE_MAX")


def space_overview_url(wiki_base: str, space_key: str) -> str:
    """Browser URL for the space overview (main) page, e.g. …/wiki/spaces/IOT/overview."""
    base = wiki_base.rstrip("/")
    key = (space_key or "").strip()
    if not key:
        return base + "/"
    return f"{base}/spaces/{quote(key, safe='')}/overview"


def fetch_it_ops_space_home_preview(
    session: requests.Session, wiki_base: str, space_key: str
) -> dict[str, str] | None:
    """
    Homepage title and overview URL (…/spaces/<key>/overview) for the dashboard block.
    """
    key = (space_key or "").strip()
    if not key:
        return None

    base = wiki_base.rstrip("/")
    sk = quote(key, safe="")
    r = session.get(
        f"{base}/rest/api/space/{sk}",
        params={"expand": "homepage"},
        timeout=90,
    )
    if not r.ok:
        print(
            f"[warn] Confluence space {key!r} (homepage): HTTP {r.status_code}",
            file=sys.stderr,
        )
        return None
    space_data = r.json()
    if not isinstance(space_data, dict):
        return None

    out: dict[str, str] = {
        "spaceKey": key,
        "pageTitle": str(space_data.get("name") or key).strip() or key,
        "pageUrl": space_overview_url(wiki_base, key),
    }
    hp = space_data.get("homepage")
    if not isinstance(hp, dict):
        return out
    cid = str(hp.get("id") or "").strip()
    if not cid:
        return out

    r2 = session.get(
        f"{base}/rest/api/content/{quote(cid, safe='')}",
        timeout=90,
    )
    if not r2.ok:
        print(
            f"[warn] Confluence homepage content {cid!r}: HTTP {r2.status_code}",
            file=sys.stderr,
        )
        return out
    content = r2.json()
    if not isinstance(content, dict):
        return out

    title = str(content.get("title") or "").strip()
    if title:
        out["pageTitle"] = title
    return out


def load_env_file() -> None:
    path = REPO_ROOT / ".env"
    if not path.is_file():
        return
    for raw in path.read_text(encoding="utf-8-sig").splitlines():
        line = raw.strip()
        if not line or line.startswith("#"):
            continue
        if line.startswith("export "):
            line = line[7:].strip()
        if "=" not in line:
            continue
        key, _, val = line.partition("=")
        key = key.strip()
        val = val.strip().strip('"').strip("'")
        if not key:
            continue
        prev = os.environ.get(key)
        if prev is None or not str(prev).strip():
            os.environ[key] = val


def wiki_base_url() -> str:
    return confluence_wiki_base()


def space_browser_url(wiki_base: str, row: dict) -> str:
    base = wiki_base.rstrip("/")
    links = row.get("_links")
    if isinstance(links, dict):
        webui = links.get("webui")
        if isinstance(webui, str) and webui.startswith("/"):
            return base + webui
    key = str(row.get("key") or "").strip()
    if key:
        return f"{base}/spaces/{quote(key, safe='')}"
    return base + "/"


def normalize_space(wiki_base: str, raw: dict) -> dict[str, str]:
    key = str(raw.get("key") or "").strip()
    name = str(raw.get("name") or "").strip() or key or "—"
    typ = str(raw.get("type") or "").strip().lower()
    return {
        "key": key,
        "name": name,
        "type": typ,
        "spaceUrl": space_browser_url(wiki_base, raw),
    }


def fetch_all_spaces(session: requests.Session, wiki_base: str) -> tuple[list[dict], list[dict], str | None]:
    personal: list[dict] = []
    global_: list[dict] = []
    err: str | None = None
    start = 0
    total_seen = 0
    api = f"{wiki_base.rstrip('/')}/rest/api/space"
    try:
        while total_seen < _max_spaces():
            r = session.get(
                api,
                params={
                    "limit": PAGE_LIMIT,
                    "start": start,
                    "status": "current",
                },
                timeout=90,
            )
            if not r.ok:
                err = f"HTTP {r.status_code} {getattr(r, 'reason', '') or ''}".strip()
                print(f"[warn] Confluence GET {api} → {err}", file=sys.stderr)
                break
            data = r.json()
            if not isinstance(data, dict):
                err = "Unexpected JSON (not an object)"
                break
            results = data.get("results")
            if not isinstance(results, list):
                err = "Missing results array"
                break
            if not results:
                break
            for raw in results:
                if not isinstance(raw, dict):
                    continue
                row = normalize_space(wiki_base, raw)
                if not row["key"]:
                    continue
                if total_seen >= _max_spaces():
                    break
                total_seen += 1
                if row["type"] == "personal":
                    personal.append(row)
                else:
                    global_.append(row)
            if total_seen >= _max_spaces():
                break
            if len(results) < PAGE_LIMIT:
                break
            start += len(results)
    except (requests.RequestException, ValueError, TypeError) as ex:
        err = str(ex)
        print(f"[warn] Confluence: {ex}", file=sys.stderr)

    def sort_key(x: dict) -> str:
        return (x.get("name") or "").lower()

    personal.sort(key=sort_key)
    global_.sort(key=sort_key)
    return personal, global_, err


def main() -> int:
    load_env_file()
    email = (
        os.environ.get("CONFLUENCE_EMAIL")
        or os.environ.get("JIRA_EMAIL")
        or ""
    ).strip()
    token = (
        os.environ.get("CONFLUENCE_API_TOKEN")
        or os.environ.get("JIRA_API_TOKEN")
        or ""
    ).strip()
    if not email or not token:
        print(
            "Missing JIRA_EMAIL / JIRA_API_TOKEN (or CONFLUENCE_EMAIL / CONFLUENCE_API_TOKEN). See env.example.",
            file=sys.stderr,
        )
        return 1

    wiki = wiki_base_url()
    session = requests.Session()
    session.auth = (email, token)
    session.headers.update({"Accept": "application/json"})

    personal, global_, api_err = fetch_all_spaces(session, wiki)

    try:
        it_ops_key = env_str("CONFLUENCE_IT_OPS_SPACE_KEY")
    except ConfigError:
        it_ops_key = ""
    it_ops_home: dict[str, str] | None = None
    try:
        it_ops_home = fetch_it_ops_space_home_preview(session, wiki, it_ops_key)
    except Exception as ex:  # noqa: BLE001
        print(f"[warn] Confluence IT Ops homepage preview: {ex}", file=sys.stderr)

    out: dict = {
        "updatedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "wikiUrl": wiki,
        "personalSpaces": personal,
        "globalSpaces": global_,
        "spacesTotal": len(personal) + len(global_),
        "spacesPersonalCount": len(personal),
        "spacesGlobalCount": len(global_),
        "confluenceError": api_err,
    }
    if it_ops_home:
        out["itOpsConfluenceHome"] = it_ops_home

    out_path = write_json_snapshot(OUT, out)
    print(f"Wrote {out_path}")
    print(f"Wrote {OUT} ({out['spacesTotal']} spaces)")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
