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;
  /** pending | skipped | completed；空＝尚未寫入，視同 pending */
  onboarding_status?: "pending" | "skipped" | "completed" | string;
  onboarding_done_at?: number;
  /** 已綁定登入管道 */
  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[];
};

/** 邀請獎勵（growth-loop） */
export type InviteReward = {
  id: string;
  invitee_uid: string;
  plan_id: string;
  points: number;
  status: "credited" | "capped" | "skipped" | string;
  created_at: number;
};

export type InviteRewardsSummary = {
  total_points: number;
  month_points: number;
  month_cap: number;
  recent: InviteReward[];
};

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

/** 成果歸因（growth-loop） */
export type OutcomeEvent = {
  id: string;
  source_type: string;
  source_id: string;
  threads_account_id?: string;
  kind: string;
  confidence: "confirmed" | "possible" | string;
  status: string;
  reply_count_delta?: number;
  like_count_delta?: number;
  followers_delta?: number;
  conversion_amount?: number;
  conversion_note?: string;
  conversion_currency?: string;
  window_ends_at: number;
  sent_at: number;
  created_at: number;
  updated_at: number;
};

export type OutcomeSummary = {
  from: number;
  to: number;
  reach: number;
  conversations: number;
  follows_possible: number;
  follows_confirmed: number;
  conversions: number;
  conversion_amount: number;
};

export type WeeklyCheckup = {
  id: string;
  week_key: string;
  status: string;
  summary: string;
  findings: { claim: string; evidence: string[] }[];
  actions: { title: string; reason: string; deeplink: string }[];
  error?: string;
  created_at: number;
  updated_at: number;
};

export type AccountHealth = {
  threads_account_id: string;
  score: number;
  level: "normal" | "warn" | "throttle" | string;
  factors: string[];
  advice: string;
  computed_at: number;
};

export type Workspace = {
  id: string;
  name: string;
  review_required: boolean;
  is_default: boolean;
  archived: boolean;
  created_at: number;
  updated_at: 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;
  /** growth-loop 資產複利 */
  learning_summary?: string;
  learning_version?: number;
  learned_from_posts_count?: number;
  last_learned_at?: number;
};

/** 留言是否已被我們回過 */
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 hide_status：NOT_HUSHED / HIDDEN */
  hide_status?: string;
};

/** Threads reply_control */
export type ThreadsReplyControl =
  | "everyone"
  | "accounts_you_follow"
  | "mentioned_only"
  | "parent_post_author_only"
  | "followers_only";

export const THREADS_REPLY_CONTROLS: ThreadsReplyControl[] = [
  "everyone",
  "accounts_you_follow",
  "mentioned_only",
  "parent_post_author_only",
  "followers_only",
];

/** 對齊 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;
  /** Threads who-can-reply */
  reply_control?: ThreadsReplyControl | string;
};

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 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 InspirationIdea = {
  id: string;
  title: string;
  hook: string;
  angle: string;
  source: "formula" | "trend" | "persona" | "viral";
  /** 來自哪個熱點（若有） */
  trend_id?: string | null;
};

/** 靈感元素庫：可重用產文材料 */
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;
  learning_summary?: string;
  learning_version?: number;
  learned_from_posts_count?: number;
  last_learned_at?: number;
};

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

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

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

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

export type ScoutRunStatus = "queued" | "running" | "succeeded" | "failed" | "cancelled";

export type ScoutShortfallReason =
  | "source_exhausted"
  | "duplicate_exhausted"
  | "relevance_exhausted"
  | "source_unavailable"
  | "limit_reached";

export type ScoutRun = {
  id: string;
  job_id: string;
  theme_key: string;
  theme_label: string;
  intent: string;
  mode: ScoutMode;
  brand_id?: string | null;
  target_count: number;
  status: ScoutRunStatus;
  searched_count: number;
  duplicate_count: number;
  irrelevant_count: number;
  eligible_count: number;
  pending_count: number;
  shortfall_count: number;
  shortfall_reasons: ScoutShortfallReason[];
  created_at: number;
  started_at?: number;
  completed_at?: number;
  error?: string;
};

export type ScoutPost = {
  id: string;
  /** 此結果所屬的獨立海巡批次；舊資料可省略。 */
  run_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）；未知時可為 0／省略 */
  posted_at?: number;
  /** 掃描命中的建立時間（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;
  /** 產品 context 全文（展示用） */
  product_context?: string;
  /** 分組／持久化用（與命中 theme_key 對齊） */
  theme_key?: string;
  theme_label?: string;
  /** 話題今日目標（則）；後端主路徑不足時會加碼／次路徑補抓 */
  target_count?: number;
};

/** 已完成的功課（可依主題找回，不消失） */
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[];
};

/* ------------------------------------------------------------------ *
 * demand-radar：商機雷達＋成交 CRM
 * 契約見 docs/product/demand-radar/spec.md §8。
 * 命名紅線：lead 已被 ThreadPlay 主帳號佔用，銷售線索一律用 Opportunity／Contact。
 * ------------------------------------------------------------------ */

/** 服務項目與價格區間 */
export type ServiceItem = {
  name: string;
  price_min?: number;
  price_max?: number;
  currency?: string;
};

export type ServiceCase = {
  title: string;
  summary?: string;
  link?: string;
};

export type ServiceFaq = {
  question: string;
  answer: string;
};

/**
 * 服務檔案：每會員一份，是判定與回覆生成的共同輸入。
 * `forbidden` 是回覆生成的硬性過濾詞，不是提示。
 */
export type ServiceProfile = {
  /** false 代表尚未建立；此時不得建立 active 雷達訂閱 */
  exists: boolean;
  services: ServiceItem[];
  cases: ServiceCase[];
  forbidden: string[];
  faq: ServiceFaq[];
  /** 縣市代碼 */
  service_areas: string[];
  remote_ok: boolean;
  availability?: string;
  tone_note?: string;
  /** unix nanoseconds UTC */
  updated_at?: number;
};

export type RadarWatchStatus = "active" | "paused" | "archived";
export type RadarWatchContextMode = "generic" | "product";
export type RadarWatchPauseReason = "user" | "product_unavailable" | "brand_unavailable";

/** 雷達訂閱：常駐關鍵字監控 */
export type RadarWatch = {
  id: string;
  terms: string[];
  exclude_terms: string[];
  /** 留空＝沿用服務檔案地區 */
  regions: string[];
  status: RadarWatchStatus;
  context_mode?: RadarWatchContextMode;
  brand_id?: string;
  product_id?: string;
  brand_name_snapshot?: string;
  product_label_snapshot?: string;
  context_bound_at?: number;
  pause_reason?: RadarWatchPauseReason;
  last_swept_at?: number;
  created_at: number;
  updated_at: number;
  /** 只在建立當下是使用者第一組訂閱且已排入首巡時為 true；非持久狀態。 */
  first_sweep_triggered?: boolean;
};

export type WatchTermSuggestion = {
  term: string;
  reason: string;
  usage: "include" | "exclude";
  basis_kind?: "audience" | "pain" | "context" | "tag" | "capability" | "exclude";
  basis_text?: string;
};

/** 一次每日巡的執行紀錄 */
export type RadarSweep = {
  id: string;
  watch_id: string;
  job_id?: string;
  path: "api" | "crawler";
  hit_count: number;
  judged_count: number;
  created_count: number;
  /** 達當日上限而未收錄的筆數；不是錯誤 */
  truncated_count: number;
  match_evaluated_count?: number;
  match_merged_count?: number;
  fit_rejected_count?: number;
  demand_input_version?: string;
  demand_map_version?: number;
  deduped_count?: number;
  prefilter_pass_count?: number;
  prefilter_review_count?: number;
  prefilter_rejected_count?: number;
  cached_judgment_count?: number;
  tombstone_matched_count?: number;
  budget_deferred_count?: number;
  sweep_status?: "complete" | "partial_budget" | "blocked_budget" | "failed" | string;
  credit_search?: number;
  credit_demand_map?: number;
  credit_judge?: number;
  credit_reply?: number;
  failed_reason?: string;
  credits_used: number;
  started_at: number;
  ended_at?: number;
};

export type IntentBand = "high" | "mid" | "low";

export type OpportunityStatus =
  | "judging"
  | "qualified"
  | "rejected"
  | "accepted"
  | "dismissed";

export type OpportunityReviewState = "pending" | "completed" | "removed";
export type OpportunityRemovalReason =
  | "pain_mismatch"
  | "provider_or_ad"
  | "stale"
  | "already_solved"
  | "duplicate"
  | "other"
  | "legacy_unknown";
export type OpportunityPriorityBand = "high" | "review" | "low";
export type OpportunitySort = "recommended" | "newest" | "oldest" | "product_fit" | "demand_intent";
export type OpportunityTimeScope = "today" | "7d" | "all";

export type OpportunityPriorityFactors = {
  pain_fit: number;
  demand_intent: number;
  evidence_quality: number;
  freshness: number;
};

export type DemandMapPhrase = {
  text: string;
  kind: string;
  basis_kind?: string;
  basis_text?: string;
  origin: "product" | "user" | "ai" | string;
  enabled: boolean;
};

export type DemandMap = {
  product_id: string;
  demand_input_version: string;
  map_version: number;
  state: "ready" | "incomplete" | "stale" | string;
  pain_phrases: DemandMapPhrase[];
  scenario_phrases: DemandMapPhrase[];
  desired_outcomes: DemandMapPhrase[];
  solution_signals: DemandMapPhrase[];
  exclusion_signals: DemandMapPhrase[];
  source_basis: string[];
  custom_phrases: DemandMapPhrase[];
  ai_enriched_at?: number;
  updated_at: number;
};

export type CostPreview = {
  preview_id: string;
  action: "sweep" | "explore" | "demand_map_enrich" | "reply" | string;
  key_mode: "platform" | "byok" | string;
  fixed_credits: number;
  min_credits: number;
  max_credits: number;
  search_calls: number;
  max_ai_candidates: number;
  remaining_credits: number;
  estimate_basis: string;
  expires_at: number;
};

/** 五問判定的單一維度 */
export type OpportunityReason = {
  dimension: "authenticity" | "intent" | "region" | "freshness" | "fit";
  score: number;
  reason: string;
};

export type OpportunityOverride = {
  from_band?: string;
  to_band?: string;
  from_status?: string;
  to_status?: string;
  actor_uid: number;
  at: number;
};

export type ProductFitBand = "strong" | "possible" | "weak";
export type ProductFitDimension = "pain" | "scenario" | "audience" | "capability";

export type ProductFitReason = {
  dimension: ProductFitDimension;
  score: number;
  reason: string;
  candidate_excerpt?: string;
  product_basis?: string;
};

export type ProductMatch = {
  brand_id: string;
  product_id: string;
  brand_name_snapshot: string;
  product_label_snapshot: string;
  brand_updated_at: number;
  product_updated_at: number;
  product_fit_score: number;
  product_fit_band: ProductFitBand;
  eligible: boolean;
  excluded: boolean;
  exclude_reason?: string;
  reasons: ProductFitReason[];
  risks: string[];
  watch_ids: string[];
  matched_terms: string[];
  matched_at: number;
};

export type ReplyVariantKind =
  | "public_comment"
  | "dm"
  | "no_sales"
  | "professional"
  | "humorous";

export type ReplyVariant = {
  id: string;
  opportunity_id: string;
  variant: ReplyVariantKind;
  text: string;
  used_at?: number;
  sent_channel?: "outbox" | "manual_copy";
  /** 只在 sent_channel=outbox 且真的排入既有 Outbox 佇列時才有值 */
  outbox_id?: string;
  created_at: number;
};

/** 手動匯入一列：貼 Threads／Facebook 貼文網址＋內文，走同一套五問判定 */
export type ManualImportItem = {
  url: string;
  text: string;
  author?: string;
  /** unix nanoseconds UTC；不填就用匯入當下時間 */
  posted_at?: number;
};

export type ManualImportResult = {
  url: string;
  opportunity_id?: string;
  status: "qualified" | "rejected" | "skipped" | "failed";
  intent_band?: IntentBand;
  intent_score?: number;
  error?: string;
};

/** 立即探索結果（POST /radar/explore） */
export type ExploreResult = {
  sweep_id: string;
  hit_count: number;
  judged_count: number;
  created_count: number;
  matched_count?: number;
  merged_count?: number;
  truncated_count: number;
  credits_used: number;
};

/** 商機：經五問判定、值得跟進的需求訊號 */
export type Opportunity = {
  id: string;
  watch_id?: string;
  source: "threads" | "manual_import" | "scout_promote";
  source_scout_post_id?: string;
  external_id: string;
  permalink: string;
  author_handle: string;
  text: string;
  posted_at: number;
  status: OpportunityStatus;
  intent_score: number;
  intent_band: IntentBand;
  /** 五維度各一條；缺任一條後端不會入庫 */
  reasons: OpportunityReason[];
  region_detected?: string;
  /** unknown 不等於 mismatch，且後端不猜測縣市 */
  region_match: "match" | "mismatch" | "unknown";
  freshness_hours: number;
  matched_service?: string;
  matched_terms: string[];
  reject_reason?: string;
  override?: OpportunityOverride;
  contact_id?: string;
  default_reply?: ReplyVariant;
  primary_brand_id?: string;
  primary_product_id?: string;
  primary_brand_name?: string;
  primary_product_label?: string;
  primary_product_fit_score?: number;
  primary_product_fit_band?: ProductFitBand;
  primary_product_overridden?: boolean;
  product_matches?: ProductMatch[];
  review_state?: OpportunityReviewState;
  previous_review_state?: OpportunityReviewState;
  removal_reason?: OpportunityRemovalReason;
  removal_note?: string;
  removed_at?: number;
  removed_by?: number;
  last_matched_at?: number;
  priority_score?: number;
  priority_band?: OpportunityPriorityBand;
  priority_factors?: OpportunityPriorityFactors;
  demand_evidence?: string[];
  demand_input_version?: string;
  demand_map_version?: number;
  created_at: number;
};

/** 今日 0 筆時後端給的原因，前端據此顯示下一步 */
export type RadarEmptyReason =
  | "not_swept_yet"
  | "all_watches_paused"
  | "no_watch"
  | "no_profile"
  | "sweep_failed"
  | "no_hit"
  | "no_eligible_product_match";

export type RadarToday = {
  stats: { total: number; high: number; mid: number; low: number };
  high: Opportunity[];
  mid: Opportunity[];
  low: Opportunity[];
  truncated_count: number;
  last_swept_at?: number;
  empty_reason?: RadarEmptyReason;
  empty_hint?: string;
};

/** CRM 七階段；needs_follow_up 是並存標記，不是第八個階段 */
export type ContactStage =
  | "new_found"
  | "engaged"
  | "dm_sent"
  | "replied"
  | "quoted"
  | "won"
  | "lost";

export type Contact = {
  id: string;
  source_platform: string;
  author_handle: string;
  display_name?: string;
  stage: ContactStage;
  needs_follow_up: boolean;
  follow_up_days: number;
  last_touch_at?: number;
  opportunity_ids: string[];
  opportunity_count: number;
  merged_from?: string[];
  top_intent_band?: IntentBand;
  top_intent_score?: number;
  created_at: number;
  updated_at: number;
};

export type ContactBrief = {
  id: string;
  source_platform: string;
  author_handle: string;
  display_name?: string;
  stage: ContactStage;
};

/** 時間軸的一則接觸紀錄；只增不改 */
export type ContactTouch = {
  id: string;
  contact_id: string;
  type: "stage" | "reply" | "note" | "conversion";
  from_stage?: ContactStage;
  to_stage?: ContactStage;
  body?: string;
  /** product context captured at this touch; immutable history */
  brand_id?: string;
  product_id?: string;
  product_label_snapshot?: string;
  opportunity_id?: string;
  actor_uid: number;
  created_at: number;
};

export type ContactOpportunityBrief = {
  id: string;
  permalink: string;
  text: string;
  intent_score: number;
  intent_band: IntentBand;
  created_at: number;
  primary_brand_name?: string;
  primary_product_label?: string;
};

export type ContactDetail = {
  contact: Contact;
  touches: ContactTouch[];
  opportunities: ContactOpportunityBrief[];
};

export type FollowUpStatus = "scheduled" | "notified" | "done" | "snoozed" | "escalated";

export type FollowUp = {
  id: string;
  contact_id: string;
  due_at: number;
  status: FollowUpStatus;
  notified_count: number;
  contact: ContactBrief;
  created_at: number;
};

/**
 * 轉換統計。任一維度樣本 < 5 時 `insufficient_sample` 為 true，
 * 此時只能顯示絕對數，不得顯示比率或排名。
 */
export type TermConversionStat = {
  term: string;
  accepted: number;
  replied: number;
  won: number;
  conversion_rate?: number;
  insufficient_sample: boolean;
};

export type VariantConversionStat = {
  variant: ReplyVariantKind;
  used: number;
  replied: number;
  won: number;
  success_rate?: number;
  insufficient_sample: boolean;
};

export type SourceConversionStat = {
  source: "radar" | "scout" | "manual_import";
  won: number;
  insufficient_sample: boolean;
};

export type CrmStatsDimension = "terms" | "variants" | "sources";

export type CrmStats = {
  terms: TermConversionStat[];
  variants: VariantConversionStat[];
  sources: SourceConversionStat[];
  /** 尚未實作／無法計算的維度；用來跟「查得到但沒有資料」區分 */
  unavailable_dimensions: CrmStatsDimension[];
};

export type ContactStageCount = {
  /** 七階段值，或跨階段檢視 needs_follow_up */
  stage: ContactStage | "needs_follow_up";
  count: number;
};
