syntax = "v1"

// M4 Studio: personas / plays / outbox / compose / own-posts / mentions

type (
	PersonaDraftPublic {
		Identity             string `json:"identity"`
		Tone                 string `json:"tone"`
		Audience             string `json:"audience"`
		Hooks                string `json:"hooks"`
		LanguageFingerprint  string `json:"language_fingerprint"`
		Rhythm               string `json:"rhythm"`
		Punctuation          string `json:"punctuation"`
		ContentPatterns      string `json:"content_patterns"`
		KnowledgeTranslation string `json:"knowledge_translation"`
		CtaStyle             string `json:"cta_style"`
		Examples             string `json:"examples"`
		Avoid                string `json:"avoid"`
	}

	StyleDimensionPublic {
		Summary  string   `json:"summary"`
		Evidence []string `json:"evidence,optional"`
	}

	PersonaStylePublic {
		Draft             PersonaDraftPublic              `json:"draft"`
		DraftText         string                          `json:"draft_text"`
		Source            string                          `json:"source"`
		BenchmarkUsername string                          `json:"benchmark_username,optional"`
		SourceLabel       string                          `json:"source_label,optional"`
		SampleCount       int                             `json:"sample_count"`
		AnalyzedAt        int64                           `json:"analyzed_at,optional"`
		SamplePreviews    []string                        `json:"sample_previews,optional"`
		// 8D：d1Tone…d8Risk → { summary, evidence }
		Dimensions        map[string]StyleDimensionPublic `json:"dimensions,optional"`
	}

	PersonaGuardPublic {
		Avoid     []string `json:"avoid"`
		MaxChars  int      `json:"max_chars"`
		BanAiTone bool     `json:"ban_ai_tone"`
	}

	PersonaPublic {
		Id                    string             `json:"id"`
		Name                  string             `json:"name"`
		Brief                 string             `json:"brief"`
		Status                string             `json:"status"`
		Style                 PersonaStylePublic `json:"style"`
		Guard                 PersonaGuardPublic `json:"guard"`
		Voice                 string             `json:"voice,optional"`
		Notes                 string             `json:"notes,optional"`
		LearningSummary       string             `json:"learning_summary,optional"`
		LearningVersion       int                `json:"learning_version,optional"`
		LearnedFromPostsCount int                `json:"learned_from_posts_count,optional"`
		LastLearnedAt         int64              `json:"last_learned_at,optional"`
		CreatedAt             int64              `json:"created_at"`
		UpdatedAt             int64              `json:"updated_at"`
	}

	PersonaListData {
		List []PersonaPublic `json:"list"`
	}

	PersonaSaveReq {
		Id     string             `json:"id,optional"`
		Name   string             `json:"name"`
		Brief  string             `json:"brief,optional"`
		Status string             `json:"status,optional"`
		Style  PersonaStylePublic `json:"style,optional"`
		Guard  PersonaGuardPublic `json:"guard,optional"`
		Voice  string             `json:"voice,optional"`
		Notes  string             `json:"notes,optional"`
	}

	PersonaIdPath {
		Id string `path:"id"`
	}

	PersonaActiveData {
		Id string `json:"id"`
	}

	PersonaSetActiveReq {
		Id string `json:"id"`
	}

	PersonaAnalyzeTextReq {
		Id          string `path:"id"`
		RawText     string `json:"raw_text"`
		SourceLabel string `json:"source_label,optional"`
	}

	PersonaAnalyzeAccountReq {
		Id       string `path:"id"`
		Username string `json:"username"`
	}

	// --- Plays ---
	ExternalTargetPublic {
		Url            string `json:"url"`
		RawUrl         string `json:"raw_url,optional"`
		Shortcode      string `json:"shortcode,optional"`
		AuthorUsername string `json:"author_username,optional"`
		TextPreview    string `json:"text_preview,optional"`
		MediaId        string `json:"media_id,optional"`
		ResolvedAt     int64  `json:"resolved_at,optional"`
	}

	PlayStepPublic {
		Id                   string   `json:"id"`
		SortOrder            int      `json:"sort_order"`
		Kind                 string   `json:"kind"`
		AccountId            string   `json:"account_id"`
		Text                 string   `json:"text"`
		DelayFromPreviousSec int      `json:"delay_from_previous_sec"`
		PersonaId            string   `json:"persona_id,optional"`
		BrandId              string   `json:"brand_id,optional"`
		ImageUrls            []string `json:"image_urls,optional"`
	}

	PlayPublic {
		Id              string                `json:"id"`
		Title           string                `json:"title"`
		Topic           string                `json:"topic"`
		Status          string                `json:"status"`
		LeadAccountId   string                `json:"lead_account_id"`
		CastAccountIds  []string              `json:"cast_account_ids"`
		TargetOwnPostId string                `json:"target_own_post_id,optional"`
		TargetExternal  ExternalTargetPublic  `json:"target_external,optional"`
		Steps           []PlayStepPublic      `json:"steps"`
		ScheduleStartAt int64                 `json:"schedule_start_at"`
		IntervalMinutes int                   `json:"interval_minutes,optional"`
		CreatedAt       int64                 `json:"created_at"`
		UpdatedAt       int64                 `json:"updated_at"`
	}

	PlayListData {
		List []PlayPublic `json:"list"`
	}

	PlaySaveReq {
		Id              string               `json:"id,optional"`
		Title           string               `json:"title"`
		Topic           string               `json:"topic,optional"`
		Status          string               `json:"status,optional"`
		LeadAccountId   string               `json:"lead_account_id"`
		CastAccountIds  []string             `json:"cast_account_ids,optional"`
		TargetOwnPostId string               `json:"target_own_post_id,optional"`
		TargetExternal  ExternalTargetPublic `json:"target_external,optional"`
		Steps           []PlayStepPublic     `json:"steps"`
		ScheduleStartAt int64                `json:"schedule_start_at,optional"`
		IntervalMinutes int                  `json:"interval_minutes,optional"`
	}

	PlayIdPath {
		Id string `path:"id"`
	}

	PlayByPostReq {
		OwnPostId string `form:"own_post_id"`
	}

	PlayByExternalReq {
		Url string `form:"url"`
	}

	PlayResolveReq {
		Url string `json:"url"`
	}

	// AI 產劇本一步（真 LLM；互回／串場共用）
	PlayGenerateStepReq {
		PersonaId    string `json:"persona_id,optional"`
		Context      string `json:"context"` // 主貼／上一則／目標文
		Topic        string `json:"topic,optional"`
		SpeakerLabel string `json:"speaker_label,optional"`
		IsLead       bool   `json:"is_lead,optional"`
		// mode: reply（預設）| root（串場主貼）
		Mode string `json:"mode,optional"`
	}

	PlayGenerateStepData {
		Text string `json:"text"`
	}

	// 一次產完整劇本（背景 job）
	PlayGenerateScriptData {
		JobId string `json:"job_id"`
		Async bool   `json:"async"`
	}

	// --- Outbox ---
	OutboxStepPublic {
		Id          string `json:"id"`
		StepId      string `json:"step_id"`
		SortOrder   int    `json:"sort_order"`
		Kind        string `json:"kind"`
		AccountId   string `json:"account_id"`
		Text        string `json:"text"`
		Status      string `json:"status"`
		Error       string `json:"error,optional"`
		ScheduledAt int64  `json:"scheduled_at,optional"`
		PublishedAt int64  `json:"published_at,optional"`
	}

	OutboxPublic {
		Id        string             `json:"id"`
		PlayId    string             `json:"play_id"`
		Title     string             `json:"title"`
		Status    string             `json:"status"`
		Steps     []OutboxStepPublic `json:"steps"`
		CreatedAt int64              `json:"created_at"`
		UpdatedAt int64              `json:"updated_at"`
	}

	OutboxListData {
		List []OutboxPublic `json:"list"`
	}

	OutboxIdPath {
		Id string `path:"id"`
	}

	OutboxRetryPath {
		Id     string `path:"id"`
		StepId string `path:"stepId"`
	}

	// --- Compose ---
	ComposeMimicReq {
		SourceText     string `json:"source_text"`
		PersonaId      string `json:"persona_id,optional"`
		// 新貼文的主題、觀點或素材；空白時由 AI 從參考文延伸不同角度
		Direction      string `json:"direction,optional"`
		// 可選：從「我的貼文」結構分析帶過來的備註（鉤子／結構／可複製點）
		StructureNotes string `json:"structure_notes,optional"`
	}

	// 仿寫改背景 Job：立刻回 job_id；完成後從 job.payload.result_text 取文
	ComposeMimicData {
		// 相容：同步路徑才有；Job 路徑為空
		Text  string `json:"text,optional"`
		JobId string `json:"job_id,optional"`
		Async bool   `json:"async,optional"`
	}

	// 人設試產：依指紋真 LLM 產主貼 + 回文（可帶新聞話題）
	ComposePersonaPreviewReq {
		PersonaId string `json:"persona_id"`
		// 手動話題；空則嘗試上網取新聞靈感
		Topic     string `json:"topic,optional"`
		// 預設 true：無 topic 時抓即時新聞標題當靈感
		UseNews   bool   `json:"use_news,optional"`
	}

	ComposePersonaPreviewData {
		Topic       string `json:"topic"`
		TopicSource string `json:"topic_source"` // news | manual | fallback
		PostText    string `json:"post_text"`
		ReplyText   string `json:"reply_text"`
		Notes       string `json:"notes,optional"`
	}

	ComposeViralReq {
		Text string `json:"text"`
	}

	ViralAnalysisPublic {
		Hooks     string `json:"hooks"`
		Structure string `json:"structure"`
		Emotion   string `json:"emotion"`
		Summary   string `json:"summary"`
		Cta       string `json:"cta,optional"`
		Copyable  string `json:"copyable,optional"`
		Risks     string `json:"risks,optional"`
	}

	ComposePublishReq {
		AccountId       string   `json:"account_id"`
		Text            string   `json:"text"`
		Title           string   `json:"title,optional"`
		ImageUrls       []string `json:"image_urls,optional"`
		ScheduleStartAt int64    `json:"schedule_start_at,optional"`
		// Threads 話題標籤（topic_tag，1～50 字，可不加 #）
		TopicTag        string   `json:"topic_tag,optional"`
	}

	// --- Own posts ---
	OwnPostReplyPublic {
		Id            string `json:"id"`
		Username      string `json:"username"`
		Text          string `json:"text"`
		CreatedAt     int64  `json:"created_at"`
		LikeCount     int    `json:"like_count,optional"`
		ReplyStatus   string `json:"reply_status,optional"`
		RepliedBy     string `json:"replied_by,optional"`
		RepliedAt     int64  `json:"replied_at,optional"`
		ParentReplyId string `json:"parent_reply_id,optional"`
		IsMine        bool   `json:"is_mine,optional"`
	}

	OwnPostPublic {
		Id             string               `json:"id"`
		AccountId      string               `json:"account_id"`
		MediaId        string               `json:"media_id,optional"`
		Text           string               `json:"text"`
		MediaType      string               `json:"media_type"`
		MediaUrl       string               `json:"media_url,optional"`
		ThumbnailUrl   string               `json:"thumbnail_url,optional"`
		Permalink      string               `json:"permalink,optional"`
		Shortcode      string               `json:"shortcode,optional"`
		TopicTag       string               `json:"topic_tag,optional"`
		LikeCount      int                  `json:"like_count"`
		ReplyCount     int                  `json:"reply_count"`
		RepostCount    int                  `json:"repost_count"`
		QuoteCount     int                  `json:"quote_count"`
		ViewCount      int                  `json:"view_count"`
		ShareCount     int                  `json:"share_count"`
		InsightsStatus string               `json:"insights_status,optional"`
		FormulaSummary string               `json:"formula_summary,optional"`
		Insight        string               `json:"insight,optional"`
		FormulaDetail  string               `json:"formula_detail,optional"`
		Replies        []OwnPostReplyPublic `json:"replies"`
		PublishedAt    int64                `json:"published_at"`
	}

	OwnPostListData {
		List []OwnPostPublic `json:"list"`
	}

	OwnPostListReq {
		AccountId string `form:"account_id,optional"`
	}

	OwnPostSyncReq {
		AccountId string `json:"account_id"`
	}

	OwnPostLastSyncedData {
		LastSyncedAt int64 `json:"last_synced_at"`
	}

	OwnPostGenerateReplyReq {
		PostId    string `json:"post_id"`
		ReplyId   string `json:"reply_id,optional"`
		PersonaId string `json:"persona_id,optional"`
	}

	OwnPostGenerateReplyData {
		Text string `json:"text"`
	}

	OwnPostSendReplyReq {
		PostId    string   `json:"post_id"`
		ReplyId   string   `json:"reply_id,optional"`
		Text      string   `json:"text"`
		AccountId string   `json:"account_id,optional"`
		ImageUrls []string `json:"image_urls,optional"`
	}

	OwnPostAnalyzeReq {
		PostId string `json:"post_id"`
	}

	// 點開貼文後再載留言（避免 sync 一次打爆 conversation API）
	OwnPostLoadRepliesReq {
		PostId string `json:"post_id"`
	}

	// --- Mentions ---
	MentionPublic {
		Id             string `json:"id"`
		AccountId      string `json:"account_id"`
		MediaId        string `json:"media_id,optional"`
		FromUsername   string `json:"from_username"`
		Text           string `json:"text"`
		ContextSnippet string `json:"context_snippet"`
		Permalink      string `json:"permalink,optional"`
		Status         string `json:"status"`
		DraftText      string `json:"draft_text,optional"`
		CreatedAt      int64  `json:"created_at"`
	}

	MentionListData {
		List []MentionPublic `json:"list"`
	}

	MentionListReq {
		AccountId string `form:"account_id,optional"`
	}

	MentionSyncReq {
		AccountId string `json:"account_id"`
	}

	MentionIdPath {
		Id string `path:"id"`
	}

	MentionGenerateReq {
		Id        string `path:"id"`
		PersonaId string `json:"persona_id,optional"`
	}

	MentionMarkRepliedReq {
		Id        string   `path:"id"`
		Text      string   `json:"text,optional"`
		ImageUrls []string `json:"image_urls,optional"`
	}
)

@server (
	jwt:        Auth
	group:      personas
	prefix:     /api/v1/personas
	middleware: AuthJWT
)
service gateway {
	@handler ListPersonas
	get / returns (PersonaListData)

	@handler GetActivePersona
	get /active returns (PersonaActiveData)

	@handler SetActivePersona
	post /active (PersonaSetActiveReq) returns (PersonaActiveData)

	@handler GetPersona
	get /:id (PersonaIdPath) returns (PersonaPublic)

	@handler SavePersona
	post / (PersonaSaveReq) returns (PersonaPublic)

	@handler RemovePersona
	delete /:id (PersonaIdPath) returns (OkData)

	@handler AnalyzePersonaText
	post /:id/analyze-text (PersonaAnalyzeTextReq) returns (PersonaPublic)

	@handler AnalyzePersonaAccount
	post /:id/analyze-account (PersonaAnalyzeAccountReq) returns (PersonaPublic)
}

@server (
	jwt:        Auth
	group:      plays
	prefix:     /api/v1/plays
	middleware: AuthJWT
)
service gateway {
	@handler ListPlays
	get / returns (PlayListData)

	@handler ListPlaysByPost
	get /by-post (PlayByPostReq) returns (PlayListData)

	@handler ListPlaysByExternal
	get /by-external (PlayByExternalReq) returns (PlayListData)

	@handler ResolveExternalLink
	post /resolve-external (PlayResolveReq) returns (ExternalTargetPublic)

	@handler GetPlay
	get /:id (PlayIdPath) returns (PlayPublic)

	@handler SavePlay
	post / (PlaySaveReq) returns (PlayPublic)

	@handler RemovePlay
	delete /:id (PlayIdPath) returns (OkData)

	@handler SubmitPlay
	post /:id/submit (PlayIdPath) returns (OutboxPublic)

	@handler GeneratePlayStep
	post /generate-step (PlayGenerateStepReq) returns (PlayGenerateStepData)

	@handler GeneratePlayScript
	post /:id/generate-script (PlayIdPath) returns (PlayGenerateScriptData)
}

@server (
	jwt:        Auth
	group:      outbox
	prefix:     /api/v1/outbox
	middleware: AuthJWT
)
service gateway {
	@handler ListOutbox
	get / returns (OutboxListData)

	@handler GetOutbox
	get /:id (OutboxIdPath) returns (OutboxPublic)

	@handler RemoveOutbox
	delete /:id (OutboxIdPath) returns (OkData)

	@handler RetryOutboxStep
	post /:id/steps/:stepId/retry (OutboxRetryPath) returns (OutboxPublic)
}

@server (
	jwt:        Auth
	group:      compose
	prefix:     /api/v1/compose
	middleware: AuthJWT
)
service gateway {
	@handler ComposeMimic
	post /mimic (ComposeMimicReq) returns (ComposeMimicData)

	@handler ComposePersonaPreview
	post /persona-preview (ComposePersonaPreviewReq) returns (ComposePersonaPreviewData)

	@handler ComposeAnalyzeViral
	post /analyze-viral (ComposeViralReq) returns (ViralAnalysisPublic)

	@handler ComposePublishSingle
	post /publish-single (ComposePublishReq) returns (OutboxPublic)
}

@server (
	jwt:        Auth
	group:      ownposts
	prefix:     /api/v1/own-posts
	middleware: AuthJWT
)
service gateway {
	@handler ListOwnPosts
	get / (OwnPostListReq) returns (OwnPostListData)

	@handler OwnPostsLastSynced
	get /last-synced-at returns (OwnPostLastSyncedData)

	@handler SyncOwnPosts
	post /sync (OwnPostSyncReq) returns (OwnPostListData)

	@handler OwnPostGenerateReply
	post /generate-reply (OwnPostGenerateReplyReq) returns (OwnPostGenerateReplyData)

	@handler OwnPostSendReply
	post /send-reply (OwnPostSendReplyReq) returns (OwnPostPublic)

	@handler OwnPostAnalyze
	post /analyze (OwnPostAnalyzeReq) returns (OwnPostPublic)

	@handler OwnPostLoadReplies
	post /load-replies (OwnPostLoadRepliesReq) returns (OwnPostPublic)
}

@server (
	jwt:        Auth
	group:      mentions
	prefix:     /api/v1/mentions
	middleware: AuthJWT
)
service gateway {
	@handler ListMentions
	get / (MentionListReq) returns (MentionListData)

	@handler SyncMentions
	post /sync (MentionSyncReq) returns (MentionListData)

	@handler MentionGenerateReply
	post /:id/generate-reply (MentionGenerateReq) returns (MentionPublic)

	@handler MentionMarkReplied
	post /:id/mark-replied (MentionMarkRepliedReq) returns (MentionPublic)

	@handler MentionSkip
	post /:id/skip (MentionIdPath) returns (MentionPublic)
}
