#!/usr/bin/env python3
"""Provision the dashboard's least-privilege n8n API key without printing it."""

from __future__ import annotations

import argparse
import base64
import hashlib
import hmac
import json
import os
import sqlite3
import tempfile
import time
import uuid
from pathlib import Path


LABEL = "Smart COD Dashboard"
SCOPES = ["workflow:list"]


def base64url(value: bytes) -> str:
    return base64.urlsafe_b64encode(value).rstrip(b"=").decode("ascii")


def sign_api_key(user_id: str, secret: str) -> str:
    header = base64url(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode())
    payload = base64url(
        json.dumps(
            {
                "sub": user_id,
                "iss": "n8n",
                "aud": "public-api",
                "jti": str(uuid.uuid4()),
                "iat": int(time.time()),
            },
            separators=(",", ":"),
        ).encode()
    )
    unsigned = f"{header}.{payload}"
    signature = base64url(hmac.new(secret.encode(), unsigned.encode(), hashlib.sha256).digest())
    return f"{unsigned}.{signature}"


def update_env(path: Path, key: str) -> None:
    original = path.read_text(encoding="utf-8")
    lines = original.splitlines()
    replacement = f"N8N_API_KEY={key}"
    found = False
    updated: list[str] = []
    for line in lines:
        if line.startswith("N8N_API_KEY="):
            updated.append(replacement)
            found = True
        else:
            updated.append(line)
    if not found:
        updated.append(replacement)

    mode = path.stat().st_mode & 0o777
    descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
    temporary = Path(temporary_name)
    try:
        with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
            handle.write("\n".join(updated) + "\n")
            handle.flush()
            os.fsync(handle.fileno())
        os.chmod(temporary, mode)
        os.replace(temporary, path)
    finally:
        temporary.unlink(missing_ok=True)


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--database", required=True, type=Path)
    parser.add_argument("--env", required=True, type=Path)
    args = parser.parse_args()

    database = args.database.resolve(strict=True)
    env_path = args.env.resolve(strict=True)
    connection = sqlite3.connect(database, timeout=30)
    connection.execute("PRAGMA busy_timeout = 30000")

    existing = connection.execute(
        'SELECT "apiKey", "scopes", "audience" FROM "user_api_keys" WHERE "label" = ?',
        (LABEL,),
    ).fetchone()
    if existing:
        api_key, scopes, audience = existing
        if json.loads(scopes or "[]") != SCOPES or audience != "public-api":
            raise RuntimeError("Existing dashboard API key does not have the expected least-privilege scope")
        action = "reused"
    else:
        owner = connection.execute(
            'SELECT "id" FROM "user" WHERE "roleSlug" = ? AND "disabled" = 0 LIMIT 1',
            ("global:owner",),
        ).fetchone()
        signing_key = connection.execute(
            'SELECT "value" FROM "deployment_key" WHERE "type" = ? AND "status" = ? ORDER BY "createdAt" DESC LIMIT 1',
            ("signing.jwt", "active"),
        ).fetchone()
        if not owner or not signing_key:
            raise RuntimeError("n8n owner or active JWT signing key was not found")

        backup_path = database.with_name(f"{database.name}.phase8-{int(time.time())}.bak")
        backup = sqlite3.connect(backup_path)
        try:
            connection.backup(backup)
        finally:
            backup.close()

        api_key = sign_api_key(owner[0], signing_key[0])
        now = time.strftime("%Y-%m-%d %H:%M:%S.000", time.gmtime())
        connection.execute(
            'INSERT INTO "user_api_keys" '
            '("id", "userId", "label", "apiKey", "createdAt", "updatedAt", "scopes", "audience") '
            'VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
            (str(uuid.uuid4()), owner[0], LABEL, api_key, now, now, json.dumps(SCOPES), "public-api"),
        )
        connection.commit()
        action = "created"

    connection.close()
    update_env(env_path, api_key)
    print(f"Dashboard API key {action}; workflow:list is the only granted API scope.")


if __name__ == "__main__":
    main()
