"""Shared credential store — a faithful mirror of the CLI's src/cli/store.ts.

Every hub reads (and signs out of) the SAME file the `three-blocks` CLI writes:
`~/.three-blocks/credentials.json`, dir 0700 / file 0600, keyed by site, with
`THREE_BLOCKS_CONFIG_DIR` overriding the location for sandboxes and e2e. That
is what makes `three-blocks login` in a terminal show up as signed-in inside
Blender or Cinema 4D, and vice-versa. Never store the token in a DCC's own
preferences — they can be exported or synced.
"""

from __future__ import annotations

import json
import os
import time
import uuid
from pathlib import Path

# The canonical host is `www.` — the apex 302-redirects to it, and a redirected
# POST degrades to a bodyless GET (api.py defends, but start canonical). Keep in
# sync with the CLI's siteUrl() default (src/cli/api.ts). Release builds rewrite
# this line per channel (scripts/stage-blender-hub.mjs) — keep its shape.
DEFAULT_SITE = "https://www.threejs-blocks.com"


def site_url() -> str:
    """API base — TB_SITE_URL lets the sandbox point at localhost."""
    return (os.environ.get("TB_SITE_URL") or DEFAULT_SITE).rstrip("/")


def config_dir() -> Path:
    override = os.environ.get("THREE_BLOCKS_CONFIG_DIR")
    if override:
        return Path(override).resolve()
    return Path.home() / ".three-blocks"


def credentials_path() -> Path:
    return config_dir() / "credentials.json"


def installation_config_path() -> Path:
    """Non-secret config shared with the CLI; never mixed into credentials."""
    return config_dir() / "config.json"


def _read_all() -> dict:
    try:
        data = json.loads(credentials_path().read_text(encoding="utf-8"))
        if isinstance(data, dict) and isinstance(data.get("credentials"), dict):
            return data
    except (OSError, ValueError):
        pass  # missing or unreadable → start fresh
    return {"version": 1, "credentials": {}}


def _write_private(path: Path, data: dict) -> None:
    directory = config_dir()
    directory.mkdir(mode=0o700, parents=True, exist_ok=True)
    try:  # best-effort tighten in case the dir already existed with looser perms
        os.chmod(directory, 0o700)
    except OSError:
        pass
    path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
    try:
        os.chmod(path, 0o600)
    except OSError:
        pass


def _write_all(data: dict) -> None:
    _write_private(credentials_path(), data)


def _read_config() -> dict:
    try:
        data = json.loads(installation_config_path().read_text(encoding="utf-8"))
        if isinstance(data, dict):
            return data
    except (OSError, ValueError):
        pass
    return {"version": 1}


def load_installation_id() -> str | None:
    """Read the CLI's stable random installation ID without creating it."""
    value = _read_config().get("installationId")
    try:
        return str(uuid.UUID(value)) if isinstance(value, str) else None
    except (ValueError, AttributeError):
        return None


def get_installation_id() -> str:
    """Share the CLI installation identity, creating it only when telemetry runs."""
    existing = load_installation_id()
    if existing:
        return existing
    data = _read_config()
    data["version"] = 1
    data["installationId"] = str(uuid.uuid4())
    _write_private(installation_config_path(), data)
    return data["installationId"]


def load_credential(site: str | None = None) -> dict | None:
    """The stored credential for a site ({token, tokenPrefix, name, savedAt}), or None."""
    entry = _read_all()["credentials"].get(site or site_url())
    return entry if entry and entry.get("token") else None


def save_credential(site: str, token: str, name: str | None = None) -> Path:
    data = _read_all()
    data["credentials"][site] = {
        "token": token,
        "tokenPrefix": f"{token[:10]}…",
        "name": name,
        "savedAt": int(time.time() * 1000),
    }
    _write_all(data)
    return credentials_path()


def clear_credential(site: str | None = None) -> bool:
    data = _read_all()
    existed = (site or site_url()) in data["credentials"]
    data["credentials"].pop(site or site_url(), None)
    _write_all(data)
    return existed
