#!/usr/bin/env python3
"""
光湖语言系统 · 苍耳API守门人 v1.0
Guanghu Language System · CA-API-Guard

设计哲学:
  苍耳/耳耳蛋/鉴影调用视频AI系统API时，必须先过验证码门。
  验证码发送到苍耳QQ邮箱（EMAIL_REDACTED@qq.com），只有苍耳本人确认后才能拿到API密钥。
  密钥本身永不暴露给AI——AI只拿到一次性的临时token，用完即焚。

协议:
  POST /api/request  → 发送验证码到苍耳邮箱 → 返回 challenge_id
  POST /api/confirm  → 校验验证码 → 返回临时API密钥
  GET  /health       → 心跳检测

部署:
  SG-001 (大脑服务器) /opt/zhuyuan/ca-api-guard/ca-api-guard.py
  监听: 0.0.0.0:8923
  守护: systemd (Restart=always)
"""

import os, json, time, hmac, hashlib, secrets, smtplib, threading
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from http.server import HTTPServer, BaseHTTPRequestHandler

# ═══════════════════════════════════════════
# 配置
# ═══════════════════════════════════════════
PORT = int(os.environ.get("CA_API_GUARD_PORT", "8923"))

# 苍耳的信息
CANGER_EMAIL = os.environ.get("CANGER_EMAIL", "EMAIL_REDACTED@qq.com")
CANGER_NAME = os.environ.get("CANGER_NAME", "苍耳")

# 耳耳蛋的信息
EED_EMAIL = os.environ.get("EED_EMAIL", "EMAIL_REDACTED@qq.com")

# QQ 邮箱 SMTP（小湖灯邮件通道）
SMTP_HOST = "smtp.qq.com"
SMTP_PORT = 465
SMTP_USER = os.environ.get("SMTP_USER", "ICE-GL∞_EMAIL_REDACTED")
SMTP_PASS = os.environ.get("QQ_SMTP_AUTH_CODE", "")

CODE_TTL = 300          # 验证码 5 分钟过期
RATE_LIMIT_WINDOW = 60  # 速率限制窗口
MAX_REQUESTS_PER_WINDOW = 3

# ═══════════════════════════════════════════
# API 密钥库（⊢ 不进仓库 · 走环境变量）
# ═══════════════════════════════════════════
API_KEYS = {
    "SC-001": os.environ.get("SC_001_JIMENG_API_KEY", ""),        # 火山即梦视频
    "SC-004": os.environ.get("SC_004_ALIYUN_QWEN_VL_KEY", ""),    # 阿里千问VL视觉
    "SC-005": os.environ.get("SC_005_ALIYUN_WANXIANG_KEY", ""),   # 阿里万相视频
    "SC-006": os.environ.get("SC_006_KLING_API_KEY", ""),         # 可灵视频
    "SC-002": os.environ.get("SC_002_VOLC_VOICE_API_KEY", ""),    # 火山语音
    "SC-007": os.environ.get("SC_007_ALIYUN_API_KEY", ""),        # 阿里百炼
}

# 运行时状态
pending_codes = {}       # {challenge_id: {code, expires_at, api_code, caller_id, description}}
rate_limit = []          # [timestamp, ...]
lock = threading.Lock()


def get_server_ip():
    try:
        import socket
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.settimeout(2)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except:
        return "unknown"


def send_email(code, caller_id, api_code, description, server_ip):
    """小湖灯 · 发送API调用验证码到苍耳邮箱"""
    # 识别调用者
    if caller_id.startswith("EED-"):
        caller_label = f"耳耳蛋 ({caller_id})"
    elif caller_id.startswith("CA-"):
        caller_label = f"鉴影 ({caller_id})"
    else:
        caller_label = caller_id

    # API 名称映射
    api_names = {
        "SC-001": "火山即梦视频生成",
        "SC-002": "火山语音复刻",
        "SC-004": "阿里千问VL视觉",
        "SC-005": "阿里万相视频生成",
        "SC-006": "可灵视频生成",
        "SC-007": "阿里百炼图像",
    }
    api_label = api_names.get(api_code, api_code)

    html = f"""<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="margin:0;padding:0;background:#f0f4f8;">
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f0f4f8;padding:30px 0;">
<tr><td align="center">
<table width="520" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(0,0,0,0.08);">
  <!-- Header -->
  <tr><td style="background:linear-gradient(135deg,#0a1628,#1a3a5c);padding:28px 32px;text-align:center;">
    <div style="font-size:18px;color:#4ec9b0;font-weight:bold;letter-spacing:2px;">光湖语言系统 · API守门人</div>
    <div style="font-size:12px;color:#8899aa;margin-top:4px;">国作登字-2026-A-00037559</div>
  </td></tr>
  <!-- Body -->
  <tr><td style="padding:32px;">
    <div style="font-size:15px;color:#333;line-height:1.8;">
      <p style="margin:0 0 12px;">👋 <b>{CANGER_NAME}</b>，有人请求调用API：</p>
      <table width="100%" cellpadding="0" cellspacing="0" style="background:#f8fafc;border-radius:8px;border:1px solid #e2e8f0;">
        <tr><td style="padding:14px 16px;font-size:14px;">
          <div style="color:#64748b;">调用者</div>
          <div style="color:#1e293b;font-weight:bold;">{caller_label}</div>
          <div style="color:#64748b;margin-top:8px;">API</div>
          <div style="color:#1e293b;font-weight:bold;">{api_label} ({api_code})</div>
          <div style="color:#64748b;margin-top:8px;">用途</div>
          <div style="color:#1e293b;">{description}</div>
          <div style="color:#64748b;margin-top:8px;">服务器</div>
          <div style="color:#1e293b;">{server_ip}</div>
        </td></tr>
      </table>
      <div style="text-align:center;margin:24px 0;">
        <div style="font-size:12px;color:#8899aa;margin-bottom:8px;">验证码 · 5分钟内有效</div>
        <div style="font-size:36px;font-weight:bold;color:#4ec9b0;letter-spacing:8px;background:#0a1628;padding:12px 24px;border-radius:8px;display:inline-block;">{code}</div>
      </div>
      <div style="background:#fff8e1;border-left:3px solid #c9a84c;padding:10px 14px;border-radius:4px;font-size:13px;color:#8b6914;">
        ⚠️ 将此验证码发给铸渊确认 → API密钥释放 → 仅当次有效
      </div>
    </div>
  </td></tr>
  <!-- Footer -->
  <tr><td style="background:#0a1628;padding:14px 32px;text-align:center;">
    <div style="font-size:11px;color:#556677;">ICE-GL∞ 光湖语言系统 · 小湖灯自动发送</div>
  </td></tr>
</table>
</td></tr>
</table>
</body>
</html>"""

    plain = f"""光湖语言系统 · API守门人

调用者: {caller_label}
API: {api_label} ({api_code})
用途: {description}
服务器: {server_ip}

验证码: {code}
有效期: 5 分钟

将此验证码发给铸渊确认 → 释放API密钥
如非本人操作，请忽略。
---
ICE-GL∞ 光湖语言系统 · 小湖灯自动发送
国作登字-2026-A-00037559"""

    msg = MIMEMultipart("alternative")
    msg["Subject"] = f"🔐 API调用验证 · {caller_label} · {api_code}"
    msg["From"] = f"光湖小湖灯 <{SMTP_USER}>"
    msg["To"] = CANGER_EMAIL
    msg.attach(MIMEText(plain, "plain", "utf-8"))
    msg.attach(MIMEText(html, "html", "utf-8"))

    try:
        server = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=15)
        server.login(SMTP_USER, SMTP_PASS)
        server.sendmail(SMTP_USER, [CANGER_EMAIL], msg.as_string())
        server.quit()
        return True
    except Exception as e:
        print(f"[EMAIL ERROR] {e}")
        return False


def check_rate(ip):
    """速率限制"""
    now = time.time()
    with lock:
        rate_limit[:] = [t for t in rate_limit if now - t < RATE_LIMIT_WINDOW]
        if len(rate_limit) >= MAX_REQUESTS_PER_WINDOW:
            return False
        rate_limit.append(now)
        return True


def generate_code():
    return ''.join([str(secrets.randbelow(10)) for _ in range(6)])


class APIHandler(BaseHTTPRequestHandler):
    def _send_json(self, code, data):
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(json.dumps(data, ensure_ascii=False).encode())

    def do_OPTIONS(self):
        self.send_response(204)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()

    def do_GET(self):
        if self.path == "/health":
            self._send_json(200, {
                "ok": True,
                "service": "ca-api-guard",
                "version": "1.0.0",
                "server": get_server_ip(),
                "target": CANGER_EMAIL
            })
        else:
            self._send_json(404, {"error": "未知路径"})

    def do_POST(self):
        ip = self.client_address[0]

        if not check_rate(ip):
            self._send_json(429, {"error": "请求过于频繁"})
            return

        # 读取请求体
        content_length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(content_length)
        try:
            data = json.loads(body)
        except:
            self._send_json(400, {"error": "无效JSON"})
            return

        if self.path == "/api/request":
            self._handle_request(data, ip)
        elif self.path == "/api/confirm":
            self._handle_confirm(data)
        else:
            self._send_json(404, {"error": "未知路径"})

    def _handle_request(self, data, ip):
        api_code = data.get("api_code", "")
        caller_id = data.get("caller_id", "unknown")
        description = data.get("description", "未提供")

        if api_code not in API_KEYS:
            self._send_json(400, {"error": f"未知API编号: {api_code}", "available": list(API_KEYS.keys())})
            return

        if not API_KEYS[api_code]:
            self._send_json(503, {"error": f"API密钥未配置: {api_code}"})
            return

        # 生成验证码
        code = generate_code()
        challenge_id = secrets.token_hex(16)
        expires_at = time.time() + CODE_TTL
        server_ip = get_server_ip()

        with lock:
            pending_codes[challenge_id] = {
                "code": code,
                "expires_at": expires_at,
                "api_code": api_code,
                "caller_id": caller_id,
                "description": description
            }
            # 清理过期
            expired = [cid for cid, v in pending_codes.items() if v["expires_at"] < time.time()]
            for cid in expired:
                del pending_codes[cid]

        # 发送邮件
        sent = send_email(code, caller_id, api_code, description, server_ip)

        self._send_json(200, {
            "ok": True,
            "challenge_id": challenge_id,
            "email_sent": sent,
            "target_email": CANGER_EMAIL,
            "caller_id": caller_id,
            "api_code": api_code,
            "expires_in": CODE_TTL,
            "message": f"验证码已发送到 {CANGER_EMAIL}，请查收后确认"
        })

    def _handle_confirm(self, data):
        challenge_id = data.get("challenge_id", "")
        code = data.get("code", "")

        with lock:
            if challenge_id not in pending_codes:
                self._send_json(404, {"error": "challenge_id 不存在或已过期"})
                return

            pending = pending_codes[challenge_id]

            if time.time() > pending["expires_at"]:
                del pending_codes[challenge_id]
                self._send_json(410, {"error": "验证码已过期"})
                return

            if pending["code"] != code:
                self._send_json(403, {"error": "验证码错误"})
                return

            # 验证通过 → 释放API密钥
            api_code = pending["api_code"]
            api_key = API_KEYS[api_code]
            del pending_codes[challenge_id]

        self._send_json(200, {
            "ok": True,
            "api_code": api_code,
            "api_key": api_key,
            "message": f"API密钥 {api_code} 已释放 · 仅当次有效 · 用完即焚"
        })


def main():
    server = HTTPServer(("0.0.0.0", PORT), APIHandler)
    print(f"🔐 苍耳API守门人 v1.0 · 端口 {PORT}")
    print(f"   目标邮箱: {CANGER_EMAIL}")
    print(f"   可用API: {[k for k, v in API_KEYS.items() if v]}")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n shutting down...")
        server.shutdown()


if __name__ == "__main__":
    main()