// Package chat 承载 Chat 模块的领域类型、Service 接口与对外暴露给其他业务模块的只读 Reader 接口。
//
// 跨模块约束（Platform spec §4.3）：其他业务模块（如 mcp）只能通过本文件暴露的
// MessageReader / ConversationReader 等 interface 横向依赖；不允许直接 import
// repository.go / service_*.go。
package chat

import (
	"context"
	"time"

	"github.com/google/uuid"

	"github.com/yan1h/agent-coding-workflow/internal/infra/llm"
)

// MessageRole 与 DB CHECK 约束一致。
type MessageRole string

const (
	RoleUser      MessageRole = "user"
	RoleAssistant MessageRole = "assistant"
)

// MessageStatus 是 messages.status 枚举。
type MessageStatus string

const (
	StatusPending   MessageStatus = "pending"
	StatusOK        MessageStatus = "ok"
	StatusError     MessageStatus = "error"
	StatusCancelled MessageStatus = "cancelled"
)

// TemplateScope 区分系统/用户模板。
type TemplateScope string

const (
	TemplateScopeSystem TemplateScope = "system"
	TemplateScopeUser   TemplateScope = "user"
)

// LLMProvider 与 DB CHECK 一致。
type LLMProvider string

const (
	ProviderAnthropic LLMProvider = "anthropic"
	ProviderOpenAI    LLMProvider = "openai"
	ProviderGemini    LLMProvider = "gemini"
)

// ModelCapabilities 描述模型能力，用于附件 capability 校验与前端能力提示。
type ModelCapabilities struct {
	Image     bool `json:"image"`
	PDF       bool `json:"pdf"`
	Audio     bool `json:"audio"`
	Reasoning bool `json:"reasoning"`
	Tools     bool `json:"tools"`
}

// LLMEndpoint 是一个 LLM 端点（provider + base_url + 加密 api_key）。
type LLMEndpoint struct {
	ID                   uuid.UUID
	Provider             LLMProvider
	DisplayName          string
	BaseURL              string
	APIKeyEncrypted      []byte
	CreatedBy            uuid.UUID
	CreatedAt, UpdatedAt time.Time
}

// LLMModel 是 endpoint 下的具体模型。
type LLMModel struct {
	ID                           uuid.UUID
	EndpointID                   uuid.UUID
	ModelID                      string
	DisplayName                  string
	Capabilities                 ModelCapabilities
	ContextWindow                int
	MaxOutputTokens              int
	PromptPricePerMillionUSD     float64
	CompletionPricePerMillionUSD float64
	ThinkingPricePerMillionUSD   float64
	IsTitleGenerator             bool
	Enabled                      bool
	SortOrder                    int
	CreatedAt, UpdatedAt         time.Time
}

// PromptTemplate 是 system prompt 模板。
type PromptTemplate struct {
	ID                   uuid.UUID
	Name, Content        string
	Scope                TemplateScope
	OwnerID              *uuid.UUID
	CreatedAt, UpdatedAt time.Time
}

// Conversation 是对话聚合根。Mounting 字段全部 NULLABLE 实现双轨制。
type Conversation struct {
	ID                   uuid.UUID
	UserID               uuid.UUID
	ProjectID            *uuid.UUID
	WorkspaceID          *uuid.UUID
	IssueID              *uuid.UUID
	RequirementID        *uuid.UUID
	ModelID              uuid.UUID
	SystemPrompt         string
	Title                *string
	TitleGeneratedAt     *time.Time
	CreatedAt, UpdatedAt time.Time
	DeletedAt            *time.Time
}

// HasFKMount 报告会话是否挂载了任一 PM/工作区 FK（project/workspace/issue/requirement）。
// composeHistory 据此决定是否调用 ContextReader 组装 FK 上下文简报。
func (c *Conversation) HasFKMount() bool {
	return c.ProjectID != nil || c.WorkspaceID != nil ||
		c.IssueID != nil || c.RequirementID != nil
}

// Attachment 是 message 的附件（user_id 必填，message_id 上传后未关联时为 nil）。
type Attachment struct {
	ID          uuid.UUID
	UserID      uuid.UUID
	MessageID   *int64 // nil = orphan
	Filename    string
	MimeType    string
	SizeBytes   int64
	SHA256      string
	StoragePath string
	CreatedAt   time.Time
}

// Message 是一轮对话消息。BIGSERIAL ID。
type Message struct {
	ID                   int64
	ConversationID       uuid.UUID
	Role                 MessageRole
	Content              string
	Thinking             *string
	ToolCalls            []byte // JSONB raw bytes; mcp 预留，MVP 始终 nil
	Status               MessageStatus
	ErrorMessage         *string
	PromptTokens         *int
	CompletionTokens     *int
	ThinkingTokens       *int
	Attachments          []Attachment // 用户可读路径填充；composeHistory 不依赖此字段
	CreatedAt, UpdatedAt time.Time
}

// UsageRecord 是 llm_usage 表一行。
type UsageRecord struct {
	ID               int64
	ConversationID   uuid.UUID
	MessageID        int64
	UserID           uuid.UUID
	EndpointID       uuid.UUID
	ModelID          uuid.UUID
	PromptTokens     int
	CompletionTokens int
	ThinkingTokens   int
	CostUSD          float64
	CreatedAt        time.Time
}

// ===== 横向暴露给其他业务模块的只读接口（mcp 用） =====

// MessageReader 暴露只读消息查询，供 mcp 模块按用户上下文返回最近消息。
type MessageReader interface {
	ListRecentMessages(ctx context.Context, userID uuid.UUID, limit int) ([]Message, error)
}

// ConversationReader 暴露只读会话查询。
type ConversationReader interface {
	ListConversationsByUser(ctx context.Context, userID uuid.UUID, limit int) ([]Conversation, error)
}

// ContextReader 是 chat 模块对 FK 上下文组装器的窄接口（adapter 在 app.go 实现，
// 内部委托 internal/brief.Composer）。给定一个挂载了 project/workspace/issue/requirement
// 的会话，返回组装好的 FK 上下文简报；无任何挂载时返回空串。messageService 在
// composeHistory 注入它的输出到有效 system prompt。chat 不直接依赖 project/brief。
type ContextReader interface {
	BriefForConversation(ctx context.Context, conv *Conversation) (string, error)
}

// ===== Service 接口（在 service_*.go 实现） =====

// ConversationService — 会话 CRUD + 标题生成 hook。
type ConversationService interface {
	Create(ctx context.Context, userID uuid.UUID, in CreateConversationInput) (*Conversation, error)
	List(ctx context.Context, userID uuid.UUID, filter ConversationFilter) ([]Conversation, error)
	Get(ctx context.Context, userID, id uuid.UUID, limit int) (*Conversation, []Message, error)
	UpdateTitle(ctx context.Context, userID, id uuid.UUID, title string) error
	SoftDelete(ctx context.Context, userID, id uuid.UUID) error
}

// CreateConversationInput 是创建会话的入参。
type CreateConversationInput struct {
	ModelID       uuid.UUID
	TemplateID    *uuid.UUID
	SystemPrompt  *string // 优先级：自定义 > 模板内容 > 空字符串
	ProjectID     *uuid.UUID
	WorkspaceID   *uuid.UUID
	IssueID       *uuid.UUID
	RequirementID *uuid.UUID
}

// ConversationFilter 列表过滤参数。
type ConversationFilter struct {
	ProjectID     *uuid.UUID
	WorkspaceID   *uuid.UUID
	IssueID       *uuid.UUID
	RequirementID *uuid.UUID
	TitleQuery    string
	BeforeID      *uuid.UUID // cursor
	Limit         int
}

// MessageService — 发消息 + 流接收 + 取消 + 重试。
type MessageService interface {
	Send(ctx context.Context, userID, conversationID uuid.UUID, content string, attachmentIDs []uuid.UUID) (userMsgID, assistantMsgID int64, err error)
	Stream(ctx context.Context, userID, conversationID uuid.UUID, sinceMessageID int64) (<-chan SSEEvent, error)
	Cancel(ctx context.Context, userID uuid.UUID, messageID int64) error
	Retry(ctx context.Context, userID uuid.UUID, messageID int64) (newAssistantMsgID int64, conversationID uuid.UUID, err error)
	ListMessages(ctx context.Context, userID, conversationID uuid.UUID, beforeID *int64, limit int) ([]Message, error)
}

// SSEEvent 是 chat handler 下行的 SSE 事件中间表示。
type SSEEvent struct {
	ID    int64  // 自增；客户端用作 ?since=
	Event string // "message_meta" | "text_delta" | "thinking_delta" | "usage" | "done" | "error"
	Data  any    // 序列化为 JSON 的 payload
}

// TemplateService — prompt_templates CRUD。
//
// 鉴权语义：user 模板仅 owner 可改/删；system 模板仅 admin 可改/删
// （isAdmin 由调用方解析后传入；Create 的 system-scope admin 校验
// 仍在 HTTP handler / MCP 工具层，与既有行为一致）。
type TemplateService interface {
	List(ctx context.Context, userID uuid.UUID) ([]PromptTemplate, error)
	Create(ctx context.Context, userID uuid.UUID, name, content string, scope TemplateScope) (*PromptTemplate, error)
	Update(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID, name, content string) error
	Delete(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID) error
}

// EndpointService — admin LLM endpoints / models CRUD（管理员守卫上层处理）。
type EndpointService interface {
	ListEndpoints(ctx context.Context) ([]LLMEndpoint, error)
	CreateEndpoint(ctx context.Context, userID uuid.UUID, in CreateEndpointInput) (*LLMEndpoint, error)
	UpdateEndpoint(ctx context.Context, id uuid.UUID, in UpdateEndpointInput) error
	DeleteEndpoint(ctx context.Context, id uuid.UUID) error
	TestEndpoint(ctx context.Context, id uuid.UUID) error // ping 上游

	ListModels(ctx context.Context, onlyEnabled bool) ([]LLMModel, error)
	CreateModel(ctx context.Context, in CreateModelInput) (*LLMModel, error)
	UpdateModel(ctx context.Context, id uuid.UUID, in UpdateModelInput) error
	DeleteModel(ctx context.Context, id uuid.UUID) error
}

// CreateEndpointInput 创建 endpoint 入参。
type CreateEndpointInput struct {
	Provider    LLMProvider
	DisplayName string
	BaseURL     string
	APIKey      string // 明文，service 内 AES-GCM 加密
}

// UpdateEndpointInput 更新 endpoint 入参；nil 字段保持不变。
type UpdateEndpointInput struct {
	DisplayName *string
	BaseURL     *string
	APIKey      *string // nil = 不修改
}

// CreateModelInput 创建 model 入参。
type CreateModelInput struct {
	EndpointID                                  uuid.UUID
	ModelID, DisplayName                        string
	Capabilities                                ModelCapabilities
	ContextWindow, MaxOutputTokens              int
	PromptPrice, CompletionPrice, ThinkingPrice float64
	IsTitleGenerator                            bool
	Enabled                                     bool
	SortOrder                                   int
}

// UpdateModelInput 更新 model 入参；nil 字段保持不变。
type UpdateModelInput struct {
	DisplayName      *string
	Capabilities     *ModelCapabilities
	ContextWindow    *int
	MaxOutputTokens  *int
	PromptPrice      *float64
	CompletionPrice  *float64
	ThinkingPrice    *float64
	IsTitleGenerator *bool
	Enabled          *bool
	SortOrder        *int
}

// UsageService — 写入 + 汇总查询。
type UsageService interface {
	Insert(ctx context.Context, in UsageRecord) error
	SummarizeByUser(ctx context.Context, from, to time.Time) ([]UserUsageRow, error)
	SummarizeByModel(ctx context.Context, from, to time.Time) ([]ModelUsageRow, error)
	SummarizeByDay(ctx context.Context, from, to time.Time) ([]DayUsageRow, error)
	UserDaily(ctx context.Context, userID uuid.UUID, from, to time.Time) ([]DayUsageRow, error)
}

// UserUsageRow 按用户聚合的用量。
type UserUsageRow struct {
	UserID                                         uuid.UUID
	PromptTokens, CompletionTokens, ThinkingTokens int
	CostUSD                                        float64
}

// ModelUsageRow 按模型聚合的用量。
type ModelUsageRow struct {
	ModelID                                        uuid.UUID
	PromptTokens, CompletionTokens, ThinkingTokens int
	CostUSD                                        float64
}

// DayUsageRow 按天聚合的用量。
type DayUsageRow struct {
	Day                                            time.Time
	PromptTokens, CompletionTokens, ThinkingTokens int
	CostUSD                                        float64
}

// ===== 流式控制接口（service_message 用） =====

// StreamerHub 暴露给 message service 的流控制能力。
type StreamerHub interface {
	Start(ctx context.Context, p StreamParams) error
	Cancel(messageID int64) bool
	Subscribe(messageID int64) (Subscription, bool)
}

// StreamParams 是启动一条流所需的所有上下文。
type StreamParams struct {
	UserID, ConversationID, EndpointID, ModelID uuid.UUID
	AssistantMessageID                          int64
	Request                                     llm.Request
	ModelMeta                                   LLMModel
}

// Subscription 是订阅者读取 ring buffer 的句柄。
type Subscription interface {
	Next(ctx context.Context, nextEventID int64) (events []SSEEvent, closed bool, err error)
}
