export type Role = "member" | "admin";

/** 島民帳號狀態（對齊 stand-alone + Harbor Desk） */
export type MemberStatus = "active" | "suspended" | "unverified" | "uninitialized";

/** 登入管道（platform 密碼 / google / line / apple） */
export type MemberIdentity = {
  login_id: string;
  platform: string;
  account_type: string;
  created_at?: number;
};

export type Member = {
  tenant_id: string;
  uid: string;
  email: string;
  phone?: string;
  display_name: string;
  nickname?: string;
  full_name?: string;
  roles: Role[];
  /** active 正常 · suspended 停權 · unverified 未驗證 */
  status?: MemberStatus;
  /** 顯示用簡短自我介紹（選填） */
  bio?: string;
  /** IANA timezone，預設 Asia/Taipei */
  timezone?: string;
  /** 是否接收系統／任務 email 通知（mock） */
  notify_email?: boolean;
  /**
   * 會員自訂頭像（mock：data URL 或 https）。空則 UI 顯示縮寫。
   */
  avatar_url?: string | null;
  /**
   * 信箱是否已驗證。未驗證不可使用 /app 功能（僅可驗證／登出）。
   */
  email_verified: boolean;
  /** 信箱驗證完成時間 unix nanoseconds UTC（選填） */
  email_verified_at?: number | null;
  phone_verified?: boolean;
  phone_verified_at?: number | null;
  gender_code?: number;
  birthdate?: number;
  national?: string;
  address?: string;
  post_code?: string;
  preferred_language?: string;
  currency?: string;
  /** 已綁定登入管道 */
  identities?: MemberIdentity[];
  /**
   * 邀請碼（分享給下線註冊／活動用）。
   * mock 每人一碼；live 由後端發放。
   */
  invite_code?: string;
  /**
   * 上線 uid（誰邀請進來）。null／缺省 = 根節點（無上線）。
   */
  parent_uid?: string | null;
};

/** 邀請網絡節點摘要（列表／樹用） */
export type InviteMemberBrief = {
  uid: string;
  email: string;
  display_name: string;
  invite_code: string;
  parent_uid: string | null;
  roles: Role[];
  status: MemberStatus;
  /** 直屬下線人數 */
  downline_count: number;
  /** 全部子孫（含間接）人數 */
  total_downline_count: number;
  /** 距根深度，0 = 根 */
  depth: number;
  joined_at: number;
  avatar_url?: string | null;
};

/** 管理員整棵邀請樹 */
export type InviteTreeNode = InviteMemberBrief & {
  children: InviteTreeNode[];
};

/** 一般會員：自己的邀請碼 + 上下線 */
export type MyInviteNetwork = {
  me: InviteMemberBrief;
  /** 上線（邀請我進來的人） */
  upline: InviteMemberBrief | null;
  /** 直屬下線 */
  downlines: InviteMemberBrief[];
  /** 全部子孫人數（含間接） */
  total_downline_count: number;
};

export type ConnectionStatus = "unknown" | "connected" | "error" | "disconnected";

export type ThreadsAccount = {
  id: string;
  username: string;
  display_name: string;
  connection: ConnectionStatus;
  is_usable: boolean;
  avatar_color: string;
  /** 頭像圖 URL；空則 UI 顯示縮寫色塊 */
  avatar_url?: string | null;
  error_message?: string;
  /** OAuth / session token 過期時間（unix nanoseconds UTC） */
  session_expires_at?: number | null;
  /** 上次成功刷新 session 的時間（unix nanoseconds UTC） */
  session_refreshed_at?: number | null;
};

export type PlayStatus = "draft" | "ready" | "scheduling" | "active" | "completed" | "partial_failed" | "archived";

export type StepKind = "root" | "reply";

export type PlayStep = {
  id: string;
  sort_order: number;
  kind: StepKind;
  account_id: string;
  text: string;
  delay_from_previous_sec: number;
  /** 可選：此則 AI 語氣人設 */
  persona_id?: string | null;
  /** 可選：此則 AI 帶入的品牌視角 */
  brand_id?: string | null;
  /** 附圖 URL（mock：本機 data URL 過長時改存佔位圖） */
  image_urls?: string[];
};

/**
 * 外部 Threads 貼文目標（貼連結進來，讓自家帳號在那則下面排回覆）。
 * Phase B mock 解析；live 再換真抓頁／media id。
 */
export type ExternalThreadTarget = {
  /** 正規化後的 permalink */
  url: string;
  /** 使用者貼上的原文 */
  raw_url?: string;
  shortcode?: string;
  author_username?: string;
  /** 正文預覽（mock 或抓取） */
  text_preview?: string;
  /** 若已解析到 numeric media id */
  media_id?: string;
  resolved_at?: number;
};

/**
 * 互回方案：
 * - 有 target_own_post_id：掛在「某則已發貼文」底下排幾則留言
 * - 有 target_external：掛在「外部 Threads 貼文」底下（貼連結）
 * - 兩者皆無：舊式「從主貼開始的整串劇本」（較少用）
 * 同一則目標可有多個方案（title 區分）。
 */
export type ThreadPlay = {
  id: string;
  /** 方案名稱，例如「方案 A · 溫和接話」 */
  title: string;
  topic: string;
  status: PlayStatus;
  /** 貼文所屬／主帳（own 時 = 該貼 account；external 時 = 預設主回帳） */
  lead_account_id: string;
  /** 可出場回覆的帳號池 */
  cast_account_ids: string[];
  /** 掛在哪則自己的貼文下 */
  target_own_post_id?: string | null;
  /** 掛在外部 Threads 貼文下（與 own 互斥） */
  target_external?: ExternalThreadTarget | null;
  steps: PlayStep[];
  schedule_start_at: number;
  /** 回覆間隔預設（分鐘），編輯用 */
  interval_minutes?: number;
  created_at: number;
  updated_at: number;
};

export type OutboxStepStatus =
  | "draft"
  | "scheduled"
  | "publishing"
  | "published"
  | "failed"
  | "cancelled"
  | "blocked";

export type OutboxStep = {
  id: string;
  step_id: string;
  sort_order: number;
  kind: StepKind;
  account_id: string;
  text: string;
  status: OutboxStepStatus;
  error?: string;
  scheduled_at?: number;
  published_at?: number;
};

export type OutboxBundleStatus = "scheduling" | "active" | "completed" | "partial_failed" | "cancelled";

export type OutboxBundle = {
  id: string;
  play_id: string;
  title: string;
  status: OutboxBundleStatus;
  steps: OutboxStep[];
  created_at: number;
  updated_at: number;
};

export type JobStatus =
  | "pending"
  | "queued"
  | "running"
  | "succeeded"
  | "failed"
  | "cancelled"
  | "cancel_requested";

export type Job = {
  id: string;
  template_type: string;
  status: JobStatus;
  progress_summary: string;
  progress_percent: number;
  error?: string;
  /** e.g. threads account id / persona id */
  ref_id?: string;
  /** job payload JSON（仿寫結果在 result_text） */
  payload?: string;
  /** scheduled run unix ns; 0 = due now */
  run_after?: number;
  created_at: number;
  updated_at: number;
  completed_at?: number;
};

export type NotificationKind = "job" | "outbox" | "system";

export type AppNotification = {
  id: string;
  title: string;
  body: string;
  kind: NotificationKind;
  ref_type: "job" | "outbox" | "none";
  ref_id?: string;
  read_at?: number | null;
  created_at: number;
};

export type DataSource = "mock" | "live";

export type TokenPair = {
  access_token: string;
  refresh_token: string;
};

/** 人設就緒狀態 */
export type PersonaStatus = "empty" | "analyzing" | "ready";

export type StyleDimKey =
  | "d1Tone"
  | "d2Structure"
  | "d3Interaction"
  | "d4Topics"
  | "d5Rhythm"
  | "d6Visual"
  | "d7Conversion"
  | "d8Risk";

export type StyleDimension = {
  summary: string;
  evidence: string[];
};

/** 語言指紋結構化欄位（產文主力） */
export type PersonaDraftFields = {
  identity: string;
  tone: string;
  audience: string;
  hooks: string;
  languageFingerprint: string;
  rhythm: string;
  punctuation: string;
  contentPatterns: string;
  knowledgeTranslation: string;
  ctaStyle: string;
  examples: string;
  avoid: string;
};

export type PersonaStyle = {
  dimensions: Partial<Record<StyleDimKey, StyleDimension>>;
  draft: PersonaDraftFields;
  /** 合成/可手改的指紋全文，優先餵 prompt */
  draftText: string;
  source: "manual" | "benchmark" | "seed";
  /** 對標帳號（公開貼文爬取來源） */
  benchmarkUsername?: string;
  /** 手動貼文來源說明 */
  sourceLabel?: string;
  sampleCount: number;
  analyzedAt?: number;
  /** 分析用的樣本摘要（可審） */
  samplePreviews?: string[];
};

export type PersonaGuard = {
  avoid: string[];
  maxChars: number;
  banAiTone: boolean;
};

/**
 * 三層人設：
 * - brief = who（定位）
 * - style = how（8D + 指紋）
 * - guard = don't
 * voice/notes 保留相容舊 seed 讀取。
 */
export type Persona = {
  id: string;
  name: string;
  brief: string;
  status: PersonaStatus;
  style: PersonaStyle;
  guard: PersonaGuard;
  /** @deprecated 相容；顯示用 tone 請用 style.draft.tone */
  voice?: string;
  /** @deprecated 相容 */
  notes?: string;
};

/** 留言是否已被我們回過 */
export type OwnPostReplyStatus = "pending" | "replied";

export type OwnPostReply = {
  id: string;
  username: string;
  text: string;
  created_at: number;
  /** 該則留言的讚（若 API 有給） */
  like_count?: number;
  /** 未回覆 / 已回覆（我們是否已回這則） */
  reply_status?: OwnPostReplyStatus;
  /** 我們回覆時用的帳號 username（mock） */
  replied_by?: string;
  replied_at?: number;
  /**
   * 父留言 id：有值 = 子留言（回某則留言）
   * 無值 = 貼文下第一層留言
   */
  parent_reply_id?: string | null;
  /** 是否為我們自己帳號發出的回覆 */
  is_mine?: boolean;
};

/** 對齊 Threads API media / insights 常見欄位（mock 先齊，接真直接 map） */
export type ThreadsMediaType =
  | "TEXT_POST"
  | "IMAGE"
  | "VIDEO"
  | "CAROUSEL_ALBUM"
  | "REPOST_FACADE"
  | "AUDIO"
  | string;

export type OwnPost = {
  id: string;
  account_id: string;
  /** Threads media id */
  media_id?: string;
  text: string;
  media_type: ThreadsMediaType;
  media_url?: string | null;
  thumbnail_url?: string | null;
  permalink?: string;
  shortcode?: string;
  topic_tag?: string;
  /** 成效（Threads insights / fields） */
  like_count: number;
  reply_count: number;
  /** 轉發 repost */
  repost_count: number;
  /** 引用 quote */
  quote_count: number;
  /** 瀏覽 views */
  view_count: number;
  /** 分享 shares（有則顯示） */
  share_count: number;
  is_quote_post?: boolean;
  is_reply?: boolean;
  /** insights 拉取狀態 */
  insights_status?: "ok" | "partial" | "unavailable" | string;
  insights_message?: string;
  formula_summary?: string;
  /** 成效洞察一句 */
  insight?: string;
  /** 爆紅／結構分析全文 */
  formula_detail?: string;
  replies: OwnPostReply[];
  published_at: number;
};

export type MentionStatus = "pending" | "replied" | "skipped";

export type MentionItem = {
  id: string;
  account_id: string;
  media_id?: string;
  from_username: string;
  text: string;
  context_snippet: string;
  permalink?: string;
  status: MentionStatus;
  draft_text?: string;
  created_at: number;
};

export type ViralSample = {
  id: string;
  author: string;
  text: string;
  like_count: number;
  reply_count: number;
  topic?: string;
};

export type ViralAnalysis = {
  hooks: string;
  structure: string;
  emotion: string;
  copyable: string;
  risks: string;
  summary: string;
};

export type ResearchHit = {
  id: string;
  title: string;
  /** 短摘 */
  snippet: string;
  url: string;
  /** 網頁內容摘要（做功課用，可比 snippet 長） */
  summary?: string;
  /** 學習重點（3～5 條，可掃讀） */
  learn_points?: string[];
  /** 回帖可直接借的鉤子 */
  reply_hooks?: string[];
  /** 來源站名 */
  source_label?: string;
  /** 與主題貼近度 */
  tier?: ScoutResearchTier;
};

/**
 * 擴充知識分層：
 * - core：最貼主題／痛點
 * - adjacent：相關周邊
 * - broad：最廣泛、背景閱讀
 */
export type ScoutResearchTier = "core" | "adjacent" | "broad";

/** 知識節點與產品／痛點的關係（圖譜邊） */
export type ScoutKnowledgeRelation =
  | "solves_pain"
  | "nearby_scene"
  | "myth"
  | "contrast"
  | "background";

/**
 * 海巡／產品知識圖譜節點：
 * 學習重點 + 來源網址 + 可選回帖鉤子
 */
export type ScoutResearchNote = {
  id: string;
  title: string;
  /** 網頁內容摘要（次要；主讀 learn_points） */
  summary: string;
  /** 學習重點：3～5 條子彈 */
  learn_points?: string[];
  /** 回帖可直接借的鉤子（1～3 句） */
  reply_hooks?: string[];
  url: string;
  source_label?: string;
  /** 從此頁抽出可海巡的詞 */
  keywords?: string[];
  /** 與主題貼近度分層 */
  tier?: ScoutResearchTier;
  /** 與產品的關係 */
  relation?: ScoutKnowledgeRelation;
};

export type InspirationIdea = {
  id: string;
  title: string;
  hook: string;
  angle: string;
  source: "formula" | "trend" | "persona" | "viral";
  /** 來自哪個熱點（若有） */
  trend_id?: string | null;
};

/** 靈感第二步：可選的開場角度（舊流程相容） */
export type InspireAngle = {
  id: string;
  hook: string;
};

/** 靈感元素庫：可重用產文材料 */
export type InspireElementKind = "persona" | "role" | "brand" | "snippet" | "trend";

export type InspireElement = {
  id: string;
  kind: InspireElementKind;
  /** 列表顯示 */
  title: string;
  /** 塞進 prompt 的文字（persona/brand 可 runtime 再展開） */
  body: string;
  /** persona_id / brand_id / trend_id */
  ref_id?: string | null;
  /** true = 進元素庫持久；false = 僅本輪 */
  reusable: boolean;
  created_at: number;
  updated_at: number;
};

export type InspireChatMessage = {
  id: string;
  role: "user" | "assistant" | "system";
  text: string;
  /** assistant 產稿時附乾淨正文 */
  draft?: { title?: string; body: string };
  created_at: number;
};

export type InspireSession = {
  id: string;
  /** 顯示名；空則 UI 用首則 user 文推 */
  title?: string;
  messages: InspireChatMessage[];
  pinned_element_ids: string[];
  created_at?: number;
  updated_at: number;
};

/** 靈感對話列表摘要（不含完整 messages） */
export type InspireSessionSummary = {
  id: string;
  title: string;
  preview: string;
  message_count: number;
  updated_at: number;
  created_at: number;
  active: boolean;
};

/** 靈感來源：外面「正在熱」的題材，不是自己腦內產文 */
export type TrendKind = "threads_tag" | "web_keyword" | "news";

export type TrendItem = {
  id: string;
  kind: TrendKind;
  /** 顯示名：#敏感肌、颱風假、無香洗髮精… */
  label: string;
  summary: string;
  /** mock 熱度 1–100 */
  heat: number;
  keywords: string[];
  /** 平台／媒體上的片段，幫助理解為什麼熱 */
  samples: string[];
  source_label: string;
  /** unix nanoseconds UTC */
  observed_at: number;
};

export type AiSettings = {
  provider: string;
  model: string;
  research_provider: string;
  research_model: string;
  /** mock only: whether a key was saved (never store real secrets in UI state beyond local) */
  api_key_configured: boolean;
  research_api_key_configured: boolean;
  /** 與 model 相同：後端明確標示上次設定的模型 */
  selected_model?: string;
  /** 這次回傳的模型清單所屬 provider */
  models_provider?: string;
  /** 模型清單（GetAi 一併帶回，不必再打 /models） */
  models?: string[];
  models_from_cache?: boolean;
  models_error?: string;
  platform_key_available?: boolean;
};

export type PlacementSettings = {
  web_search_provider: "brave" | "exa";
  expand_strategy: "brave" | "llm" | "hybrid";
  brave_api_key_configured: boolean;
  exa_api_key_configured: boolean;
  dev_mode_enabled: boolean;
};

export type Brand = {
  id: string;
  display_name: string;
  brief: string;
  /** 給誰：敏感受眾、遠端上班族… */
  target_audience?: string;
  /** 這輪目標：軟性經驗分享、導連結… */
  goals?: string;
};

/**
 * 品牌下的可分享物：海巡用「痛 ↔ 解」找人、寫外展。
 * 對齊舊 BrandsPage 四欄 + pain_points。
 */
export type BrandProduct = {
  id: string;
  brand_id: string;
  /** 產品標籤／名稱 */
  label: string;
  /** 賣點與使用情境（寫草稿） */
  product_context: string;
  /** 對方貼文常出現的詞（掃／打分） */
  match_tags: string[];
  /** 對方在煩什麼（找人、產關鍵字） */
  pain_points: string[];
  /** 可分享連結（選填） */
  placement_url?: string;
  created_at: number;
  updated_at: number;
};

export type ScoutTopic = {
  id: string;
  brand_id: string;
  name: string;
  keywords: string[];
  /** 此主題預設主推的產品 */
  preferred_product_id?: string | null;
};

export type ScoutOutreachStatus = "new" | "drafted" | "queued" | "published" | "skipped";

/**
 * 探查模式：
 * - product：痛點＋產品置入
 * - theme：主題／痛點接話（可不帶產品）
 * - activity：關鍵字活躍（刷存在感、養帳號）
 */
export type ScoutMode = "product" | "theme" | "activity";

/** 使用者選的探查目的（UI） */
export type ScoutPurpose = "value" | "activity";

export type ScoutPost = {
  id: string;
  /** @deprecated 保留相容；新掃描以 brand_id 為主 */
  topic_id?: string;
  /** 主題模式可無品牌 */
  brand_id?: string | null;
  author: string;
  text: string;
  search_tag: string;
  opportunity: string;
  outreach_status: ScoutOutreachStatus;
  draft_text?: string;
  /** 0–100 契合分（mock 打分） */
  score: number;
  matched_product_id?: string | null;
  matched_product_label?: string | null;
  /** 為何命中 */
  match_reason?: string;
  scout_mode?: ScoutMode;
  /** 這輪意圖摘要（可選） */
  intent_snippet?: string;
  /** 分組用：同一輪海巡相同 */
  theme_key?: string;
  /** 分組顯示名：主題／產品／活躍關鍵字 */
  theme_label?: string;
  /** 掃描來源，例如 api 或 crawler */
  scan_path?: string;
  /** 後端分類結果；舊資料或目前契約未提供時省略 */
  classification?: string;
  /** 原始 Threads 貼文連結 */
  permalink?: string;
  /** 掃描命中的建立時間（UTC unix nanoseconds） */
  created_at?: number;
  /** 實際發出回覆的時間（UTC unix nanoseconds）；舊資料可能沒有 */
  published_at?: number;
};

/**
 * 一輪探查：意圖 + 可審知識（勾選後才掃）
 */
export type ScoutRunBrief = {
  intent: string;
  mode: ScoutMode;
  brand_id?: string | null;
  product_id?: string | null;
  product_label?: string | null;
  /** 展示：產品能解的痛（A）或主題焦點詞（B） */
  pains: string[];
  /** 對方常講／興趣詞 */
  tags: string[];
  /** 周邊知識 */
  periphery: string[];
  /** 預設全勾；使用者可卸 */
  scan_terms: string[];
  /** A：置入原則 */
  placement_note?: string;
  /** B：回應姿態 */
  response_stance?: string;
  /**
   * 做功課：擴充知識（網頁摘要＋網址）
   * 對齊舊海巡 research 詳細度
   */
  research_notes?: ScoutResearchNote[];
  /** 產品 context 全文（展示用） */
  product_context?: string;
  /** 對方常講的話（match_tags 詳列） */
  match_tags_detail?: string[];
  /** 分組／持久化用（與命中 theme_key 對齊） */
  theme_key?: string;
  theme_label?: string;
};

/** 已完成的功課（可依主題找回，不消失） */
export type ScoutHomeworkRecord = {
  theme_key: string;
  theme_label: string;
  purpose: ScoutPurpose;
  brief: ScoutRunBrief;
  /** unix nanoseconds UTC */
  created_at: number;
};

/** @deprecated 舊掃描前 context；新流程用 ScoutRunBrief */
export type ScoutScanContext = {
  brand_id: string;
  product_ids: string[];
  pains: string[];
  tags: string[];
  /** 輕量延伸詞（相關問法） */
  expand_terms: string[];
};
