// 共通型定義

export type SubscriberStatus = "active" | "unsubscribed" | "bounced";
/**
 * SMS 送信可否（メールの status とは独立管理）。
 * 特定電子メール法はSMSにも適用されるため「番号を持っている」≠「送信してよい」。
 * active になるのは明示的な同意（sms_consent）を受け取ったときだけ。
 */
export type SmsStatus = "none" | "active" | "unsubscribed";
/** ステップの配信チャネル */
export type StepChannel = "email" | "sms";
export type BroadcastStatus =
  | "draft"
  | "scheduled"
  | "sending"
  | "sent"
  | "canceled";
export type SendStatus = "pending" | "sent" | "failed" | "skipped";
export type EnrollmentStatus = "active" | "completed" | "canceled";

export interface Subscriber {
  id: number;
  email: string;
  name: string | null;
  status: SubscriberStatus;
  source: string | null;
  unsub_token: string;
  created_at: number;
  // --- v4: SMS ---
  phone: string | null; // E.164 (+819012345678)
  sms_status: SmsStatus;
  sms_consent_at: number | null; // 同意日時（特電法/特商法の記録保存義務）
  sms_consent_source: string | null; // 同意の取得経路
}

export interface List {
  id: number;
  name: string;
  created_at: number;
}

export interface Broadcast {
  id: number;
  name: string;
  subject: string;
  body_html: string;
  list_id: number;
  status: BroadcastStatus;
  scheduled_at: number | null;
  approved: boolean;
  sent_at: number | null;
  created_at: number;
  // --- v5: チャネル分離 ---
  channel: StepChannel; // email | sms（既定 email で後方互換）
  body_text: string | null; // channel=sms のときの本文
}

export interface BroadcastSend {
  id: number;
  broadcast_id: number;
  subscriber_id: number;
  status: SendStatus;
  resend_id: string | null;
  error: string | null;
  sent_at: number | null;
}

export interface StepCampaign {
  id: number;
  name: string;
  list_id: number;
  trigger: string;
  status: "active" | "paused";
  created_at: number;
  /**
   * v5: シナリオの主チャネル。新規ステップの既定値と、管理画面の一覧の振り分けに使う。
   * ステップ単位の channel は別に持つため、1シナリオ内にメールとSMSを混在させることは可能。
   */
  channel: StepChannel;
}

/**
 * ステップ配信タイミングの方式（エキスパ/UTAGE準拠）
 * - immediate: 登録（または前ステップ配信）の直後
 * - relative:  登録（または前ステップ配信）から delay_minutes 分後
 * - day_time:  登録（または前ステップ配信）から delay_days 日後の send_time（JST）
 */
export type StepTimingType = "immediate" | "relative" | "day_time";

export interface StepEmail {
  id: number;
  campaign_id: number;
  step_order: number;
  delay_minutes: number;
  subject: string;
  body_html: string;
  timing_type: StepTimingType;
  delay_days: number;
  send_time: string | null; // 'HH:MM'（JST, timing_type=day_time のとき使用）
  // --- v4: SMS ---
  channel: StepChannel; // email | sms（既定 email で後方互換）
  body_text: string | null; // channel=sms のときの本文（プレーンテキスト）
}

export interface StepEnrollment {
  id: number;
  campaign_id: number;
  subscriber_id: number;
  current_step: number;
  status: EnrollmentStatus;
  next_send_at: number;
  enrolled_at: number;
}

export interface Hook {
  id: number;
  token: string;
  list_id: number;
  campaign_id: number | null;
  name: string | null;
  created_at: number;
}

// ---------- ポイント & 抽選（クリックブースター相当） ----------

export type PointKind = "open" | "click" | "signup" | "manual" | "lottery" | "exchange";

export interface PointEvent {
  id: number;
  subscriber_id: number;
  points: number;
  kind: PointKind;
  dedupe_key: string | null;
  note: string | null;
  created_at: number;
}

export interface TrackedLink {
  id: number;
  scope: "bc" | "step";
  ref_id: number;
  url: string;
  created_at: number;
}

export interface Lottery {
  id: number;
  name: string;
  description: string | null;
  cost_points: number;
  status: "open" | "closed";
  created_at: number;
}

export interface LotteryPrize {
  id: number;
  lottery_id: number;
  name: string;
  description: string | null;
  probability: number; // 当選確率（%）
  max_winners: number; // 0=無制限
  active: boolean;
  created_at: number;
}

export interface LotteryDraw {
  id: number;
  lottery_id: number;
  subscriber_id: number;
  prize_id: number | null; // null=はずれ
  points_spent: number;
  fulfilled: boolean;
  drawn_at: number;
}

export interface ExchangeItem {
  id: number;
  name: string;
  description: string | null;
  cost_points: number;
  stock: number; // -1=無制限
  active: boolean;
  created_at: number;
}

export interface ExchangeRedemption {
  id: number;
  item_id: number;
  subscriber_id: number;
  points_spent: number;
  status: "pending" | "done" | "canceled";
  created_at: number;
}

/** ポイント付与ルール（settings に保存、管理画面で変更可能） */
export interface PointRules {
  openPoints: number; // メール開封で付与
  clickPoints: number; // 本文URLクリックで付与
  signupPoints: number; // 新規登録ボーナス
}

/** 送信ゲートと上限の判定に使う設定 */
export interface RuntimeConfig {
  dryRun: boolean;
  mailFrom: string;
  senderInfo: string;
  maxSendsPerTick: number;
  maxSendsPerDay: number;
  appBaseUrl: string;
  // --- v4: SMS（メール側の設定とは完全に独立させる） ---
  /** SMS実送信ゲート（既定 true = 送らない）。メールの dryRun とは別管理 */
  smsDryRun: boolean;
  /** 1ティックあたりのSMS送信上限 */
  maxSmsPerTick: number;
  /** 1日あたりのSMS送信上限（SMSは1通≒10円のためコスト暴走防止の生命線） */
  maxSmsPerDay: number;
  /** 管理画面のコスト概算表示に使う1通（1セグメント）あたりの円単価 */
  smsCostPerSegment: number;
  /**
   * SMS本文に載せる送信者名。未設定なら senderInfo を使う。
   * SMSは70文字ごとに課金されるため、住所を含む長い senderInfo をそのまま載せると
   * 本文0文字でも2通ぶん課金される。ここには「名称」だけの短い文字列を入れ、
   * 住所などの詳細は配信停止ページ（/u/:token）に表示して法定表示を満たす。
   */
  smsSenderInfo: string;
  /**
   * SMS本文の配信停止URLに使うベースURL。未設定なら appBaseUrl を使う。
   * 短い独自ドメインを同じWorkerに向けて設定すると、1通あたりのコストを下げられる。
   */
  smsBaseUrl: string;
}

/** Mailer に渡す1通分 */
export interface OutMessage {
  to: string;
  subject: string;
  html: string;
}

/** SmsSender に渡す1通分 */
export interface OutSms {
  to: string; // E.164
  body: string;
}

export interface SendResult {
  ok: boolean;
  id?: string;
  error?: string;
}
