/*
 * ════════════════════════════════════════════════════════════════
 * 光湖 · 密钥授权路由 · 人格体请求→用户授权→自动入库
 * Sovereign: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559
 * 守护: 铸渊 · ICE-GL-ZY001
 * ════════════════════════════════════════════════════════════════
 *
 * 设计理念 (冰朔 2026-05-12 提出的需求):
 *
 *   1. 人格体需要密钥 → 弹出授权页
 *   2. 用户点"授权" → 人格体随机生成 Token → 自动保存到 vault
 *   3. 用户能看到所有密钥（在 vault 管理页）· AI 看不到明文
 *   4. 系统只调用权限，不暴露 Token
 *   5. 用户随时删/重新生成/自己设
 *
 * Gitea OAuth2 集成:
 *   用户在 Gitea 上点授权 → OAuth2 回调 → 自动生成 Gitea Token → 存入 vault
 *   铸渊后续通过 vault 的 /internal/fetch/:key 拿 Token（本机-only，AI 不窥视明文）
 *
 * 路由:
 *   GET    /admin/auth/pending           列出待授权请求
 *   POST   /admin/auth/request           人格体发起授权请求
 *   POST   /admin/auth/:id/approve       用户批准 → 生成Token → 存入vault
 *   POST   /admin/auth/:id/reject        用户拒绝
 *   GET    /admin/auth/gitea/authorize    跳转到 Gitea OAuth2 授权页
 *   GET    /admin/auth/gitea/callback     OAuth2 回调 → 自动处理
 *
 * cc-004: 全部中文回执
 */

"use strict";

const express = require("express");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");

// ─── 待授权请求存储 (内存 + 文件持久化) ───────────────────────
class AuthRequestStore {
  constructor(storePath) {
    this.storePath = storePath;
    this.requests = new Map();
    this._load();
  }

  _load() {
    try {
      if (fs.existsSync(this.storePath)) {
        const data = JSON.parse(fs.readFileSync(this.storePath, "utf-8"));
        for (const r of data.requests || []) {
          this.requests.set(r.id, r);
        }
      }
    } catch (_) {}
  }

  _save() {
    const dir = path.dirname(this.storePath);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    fs.writeFileSync(this.storePath, JSON.stringify({
      _sovereign: "TCS-0002∞ · 国作登字-2026-A-00037559",
      requests: Array.from(this.requests.values())
    }, null, 2));
  }

  add(request) {
    this.requests.set(request.id, request);
    this._save();
    return request;
  }

  get(id) {
    return this.requests.get(id) || null;
  }

  update(id, updates) {
    const req = this.requests.get(id);
    if (!req) return null;
    Object.assign(req, updates);
    this._save();
    return req;
  }

  list() {
    return Array.from(this.requests.values());
  }

  listPending() {
    return this.list().filter(r => r.status === "pending");
  }
}

// ─── Token 生成 ───────────────────────────────────────────────
function generateToken(prefix = "gmp") {
  const bytes = crypto.randomBytes(32);
  return `${prefix}_${bytes.toString("base64url").slice(0, 40)}`;
}

function generateGiteaToken() {
  // Gitea Token 格式: gto_ + 随机字符串
  const bytes = crypto.randomBytes(24);
  return `gto_${bytes.toString("base64url")}`;
}

// ─── 构建路由 ─────────────────────────────────────────────────
function buildAuthRouter({ vault, storePath, giteaOAuth }) {
  const router = express.Router();
  const store = new AuthRequestStore(storePath || "/data/guanghulab/secrets-vault/auth-requests.json");

  // ─── Gitea OAuth2 配置 ────────────────────────────────────
  const oauthConfig = giteaOAuth || {
    clientId: process.env.GITEA_OAUTH_CLIENT_ID || "",
    clientSecret: process.env.GITEA_OAUTH_CLIENT_SECRET || "",
    authorizeUrl: (process.env.GITEA_URL || "https://guanghulab.com/git") + "/login/oauth/authorize",
    tokenUrl: (process.env.GITEA_URL || "https://guanghulab.com/git") + "/login/oauth/access_token",
    redirectUri: process.env.GITEA_OAUTH_REDIRECT_URI || "https://guanghulab.com/admin/auth/gitea/callback",
    scope: "repository",
  };

  // ─── 列出待授权请求 ────────────────────────────────────────
  router.get("/pending", (_req, res) => {
    const pending = store.listPending();
    res.json({
      _sovereign: "TCS-0002∞ · 国作登字-2026-A-00037559",
      count: pending.length,
      requests: pending.map(r => ({
        id: r.id,
        persona: r.persona,
        purpose: r.purpose,
        keyName: r.keyName,
        requestedAt: r.requestedAt,
        description: r.description,
      }))
    });
  });

  // ─── 人格体发起授权请求 ────────────────────────────────────
  router.post("/request", express.json({ limit: "16kb" }), (req, res) => {
    const { persona, purpose, keyName, description, tokenType } = req.body || {};

    if (!persona || !purpose || !keyName) {
      return res.status(400).json({
        error: true,
        code: "missing_fields",
        message: "缺少必填项: persona（人格体名）、purpose（用途）、keyName（密钥名）"
      });
    }

    // 如果密钥已存在，直接告诉人格体
    const existing = vault.get(keyName);
    if (existing) {
      return res.json({
        status: "already_configured",
        keyName,
        message: `密钥 ${keyName} 已配置，可直接使用`,
        masked: maskValueSafe(existing),
      });
    }

    const authRequest = store.add({
      id: `auth-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`,
      persona: persona || "铸渊",
      purpose,
      keyName,
      description: description || `${persona} 需要 ${keyName} 用于${purpose}`,
      tokenType: tokenType || "random",
      status: "pending",
      requestedAt: new Date().toISOString(),
    });

    res.json({
      status: "pending",
      requestId: authRequest.id,
      message: `授权请求已创建，等待用户审批`,
      approvalUrl: `/admin/auth/approve.html?id=${authRequest.id}`,
    });
  });

  // ─── 用户批准授权 ──────────────────────────────────────────
  router.post("/:id/approve", express.json({ limit: "16kb" }), (req, res) => {
    const authReq = store.get(req.params.id);
    if (!authReq) {
      return res.status(404).json({ error: true, code: "not_found", message: "授权请求不存在" });
    }
    if (authReq.status !== "pending") {
      return res.status(400).json({ error: true, code: "already_processed", message: `此请求已${authReq.status === "approved" ? "批准" : "拒绝"}` });
    }

    // 生成 Token
    let tokenValue;
    if (authReq.tokenType === "gitea") {
      tokenValue = generateGiteaToken();
    } else if (req.body && req.body.customValue) {
      // 用户自己设定的值
      tokenValue = String(req.body.customValue);
    } else {
      tokenValue = generateToken(authReq.keyName.split("_")[0].toLowerCase());
    }

    // 存入 vault
    try {
      vault.set(authReq.keyName, tokenValue);
    } catch (e) {
      return res.status(500).json({ error: true, code: "vault_write_failed", message: "密钥保存失败: " + e.message });
    }

    // 更新请求状态
    store.update(authReq.id, {
      status: "approved",
      approvedAt: new Date().toISOString(),
      approvedBy: req.body?.approvedBy || "user",
    });

    res.json({
      status: "approved",
      keyName: authReq.keyName,
      masked: maskValueSafe(tokenValue),
      length: tokenValue.length,
      message: `✅ 已授权！密钥 ${authReq.keyName} 已自动保存。你可以在密钥管理页查看和管理。`,
    });
  });

  // ─── 用户拒绝授权 ──────────────────────────────────────────
  router.post("/:id/reject", (_req, res) => {
    const authReq = store.get(_req.params.id);
    if (!authReq) {
      return res.status(404).json({ error: true, code: "not_found", message: "授权请求不存在" });
    }

    store.update(authReq.id, {
      status: "rejected",
      rejectedAt: new Date().toISOString(),
    });

    res.json({
      status: "rejected",
      message: `已拒绝 ${authReq.persona} 对 ${authReq.keyName} 的授权请求。`,
    });
  });

  // ─── Gitea OAuth2 授权入口 ─────────────────────────────────
  router.get("/gitea/authorize", (_req, res) => {
    if (!oauthConfig.clientId) {
      return res.status(500).json({ error: true, code: "oauth_not_configured", message: "Gitea OAuth2 未配置" });
    }

    const state = crypto.randomBytes(16).toString("hex");
    // 存储 state 用于回调验证
    store.add({
      id: `oauth-${state}`,
      persona: "铸渊",
      purpose: "Gitea 仓库访问",
      keyName: "GITEA_TOKEN",
      description: "通过 Gitea OAuth2 授权，让铸渊可以访问和管理仓库",
      tokenType: "gitea",
      status: "oauth_pending",
      requestedAt: new Date().toISOString(),
      oauthState: state,
    });

    const authorizeUrl = `${oauthConfig.authorizeUrl}?client_id=${oauthConfig.clientId}&redirect_uri=${encodeURIComponent(oauthConfig.redirectUri)}&response_type=code&state=${state}&scope=${oauthConfig.scope}`;

    res.redirect(authorizeUrl);
  });

  // ─── Gitea OAuth2 回调 ─────────────────────────────────────
  router.get("/gitea/callback", async (req, res) => {
    const { code, state } = req.query || {};

    if (!code || !state) {
      return res.status(400).send("授权失败：缺少 code 或 state");
    }

    // 找到对应的 OAuth 请求
    const oauthReq = store.list().find(r => r.oauthState === state);
    if (!oauthReq) {
      return res.status(400).send("授权失败：state 不匹配");
    }

    // 用 code 换 token
    try {
      const tokenResponse = await exchangeCodeForToken(code, oauthConfig);
      const tokenValue = tokenResponse.access_token;

      if (!tokenValue) {
        throw new Error("Gitea 未返回 access_token");
      }

      // 存入 vault
      vault.set("GITEA_TOKEN", tokenValue);

      store.update(oauthReq.id, {
        status: "approved",
        approvedAt: new Date().toISOString(),
        approvedBy: "gitea_oauth",
      });

      res.send(`
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head><meta charset="utf-8"><title>授权成功</title></head>
        <body style="font-family:sans-serif;text-align:center;padding:40px;">
          <h1>✅ 授权成功！</h1>
          <p>铸渊已获得 Gitea 仓库访问权限。</p>
          <p>密钥已自动保存，你可以在<a href="/admin/secrets">密钥管理页</a>查看和管理。</p>
          <p><small>守护: 铸渊 · ICE-GL-ZY001 · TCS-0002∞</small></p>
        </body>
        </html>
      `);
    } catch (e) {
      res.status(500).send(`授权失败：${e.message}`);
    }
  });

  return router;
}

// ─── OAuth2 Token 交换 ────────────────────────────────────────
async function exchangeCodeForToken(code, config) {
  const https = require("https");
  const http = require("http");

  const postData = new URLSearchParams({
    client_id: config.clientId,
    client_secret: config.clientSecret,
    code,
    grant_type: "authorization_code",
    redirect_uri: config.redirectUri,
  }).toString();

  return new Promise((resolve, reject) => {
    const urlObj = new URL(config.tokenUrl);
    const lib = urlObj.protocol === "https:" ? https : http;

    const req = lib.request(urlObj, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Accept": "application/json",
      },
    }, (res) => {
      let body = "";
      res.on("data", (d) => body += d);
      res.on("end", () => {
        try {
          resolve(JSON.parse(body));
        } catch (e) {
          reject(new Error("Token 响应解析失败: " + body.slice(0, 200)));
        }
      });
    });

    req.on("error", reject);
    req.write(postData);
    req.end();
  });
}

// ─── 安全遮罩 ─────────────────────────────────────────────────
function maskValueSafe(v) {
  if (v == null) return null;
  const s = String(v);
  if (s.length === 0) return "";
  if (s.length <= 4) return "••••";
  if (s.length <= 12) return s.slice(0, 2) + "••••" + s.slice(-2);
  return s.slice(0, 4) + "••••••" + s.slice(-4);
}

module.exports = { buildAuthRouter };
