// Package mcp 实现 MCP（Model Context Protocol）Server 模块。
//
// 模块结构（spec §2.4 决策）：单 package + 多文件，与 acp / chat 同模式。
// MCP 协议合规性由官方 github.com/modelcontextprotocol/go-sdk 承担；
// 本模块只负责：
//   - mcp_tokens 表的 CRUD（admin 后台管理）
//   - system token 在 ACP session 启动时签发、终止时撤销
//   - 鉴权 middleware（bearer / ?token= → user_id + scope）
//   - 速率限制
//   - 工具/prompts/resources 的业务桥接（调 project / chat 现有 service）
//
// 三类核心抽象：
//   - Token：DB 行记录，token plaintext 仅签发时返回一次
//   - Scope：JSONB 形态的工具白名单 + project_ids 限定
//   - Issuer：'system' 或 'admin'，决定生命期与是否绑定 ACP session
package mcp

import (
	"time"

	"github.com/google/uuid"
)

// Issuer 是 mcp_tokens.issuer 枚举。
type Issuer string

const (
	// IssuerSystem token 由 ACP supervisor 起 session 时自动签发，
	// 必须绑定 acp_session_id，session 终止即撤销。
	IssuerSystem Issuer = "system"
	// IssuerAdmin token 由 admin 后台手动签发给团队成员（含 admin 自己）。
	// 不绑定 ACP session，长期有效，admin 或持有者可主动撤销。
	IssuerAdmin Issuer = "admin"
)

// IsValid 判断 issuer 是否为合法枚举值。
func (i Issuer) IsValid() bool {
	return i == IssuerSystem || i == IssuerAdmin
}

// Scope 是 mcp_tokens.scope JSONB 列的 Go 反射。
//
// 语义：
//   - Tools 为 nil（JSON null/缺省）= 全工具允许；非 nil 但空切片 = 全部禁用
//   - ProjectIDs 为 nil = 全 project 允许；非 nil 空切片 = 全部禁用
//
// JSON marshal 时 nil 字段用 omitempty 抹掉，与 spec §3.1 表头注释一致。
type Scope struct {
	Tools      []string    `json:"tools,omitempty"`
	ProjectIDs []uuid.UUID `json:"project_ids,omitempty"`
}

// AllowsTool 检查工具名是否在白名单。Tools 为 nil 时全允许。
func (s *Scope) AllowsTool(name string) bool {
	if s == nil || s.Tools == nil {
		return true
	}
	for _, t := range s.Tools {
		if t == name {
			return true
		}
	}
	return false
}

// AllowsProject 检查 project ID 是否在限定列表。ProjectIDs 为 nil 时全允许。
func (s *Scope) AllowsProject(id uuid.UUID) bool {
	if s == nil || s.ProjectIDs == nil {
		return true
	}
	for _, p := range s.ProjectIDs {
		if p == id {
			return true
		}
	}
	return false
}

// Token 是 mcp_tokens 表的 Go 表示。
//
// TokenHash 是 SHA-256(plaintext)；plaintext 仅在 IssueAdmin/IssueSystemToken
// 的返回值里出现一次，DB 与本结构都不持有 plaintext。
type Token struct {
	ID            uuid.UUID
	TokenHash     []byte
	UserID        uuid.UUID
	Name          string
	Issuer        Issuer
	Scope         Scope
	ACPSessionID  *uuid.UUID
	ExpiresAt     *time.Time
	LastUsedAt    *time.Time
	RevokedAt     *time.Time
	CreatedBy     uuid.UUID
	CreatedAt     time.Time
}

// IsActive 报告 token 当前是否可用（未撤销 + 未过期）。
// now 显式注入便于测试；生产调用方传 time.Now()。
func (t *Token) IsActive(now time.Time) bool {
	if t.RevokedAt != nil {
		return false
	}
	if t.ExpiresAt != nil && !t.ExpiresAt.After(now) {
		return false
	}
	return true
}
