import {
  int,
  json,
  mysqlTable,
  text,
  timestamp,
  varchar,
} from "drizzle-orm/mysql-core";

/**
 * msg 服务 Outbox + 消费幂等 Schema。
 *
 * 仲裁依据：
 * - 02-architecture-design.md §3.1.4（msg_outbox_events）
 * - 02-architecture-design.md §3.1.5（processed_events）
 * - G11：ID 用 cuid2（varchar(32)）
 *
 * 参照 core-edu OutboxPublisher 模式：本地 TOPIC_MAP 多 topic 路由。
 */

/** Outbox 记录状态机 */
export type OutboxStatus = "pending" | "published" | "failed";

/**
 * msg_outbox_events 表：事务性 Outbox。
 *
 * 业务事务内写入，OutboxPublisher 轮询 pending 记录投递到 Kafka。
 * eventType 经 TOPIC_MAP 路由到不同 topic（edu.notification.sent/read/recalled/failed）。
 */
export const outboxEvents = mysqlTable("msg_outbox_events", {
  eventId: varchar("event_id", { length: 64 }).notNull().primaryKey(),
  aggregateType: varchar("aggregate_type", { length: 64 }).notNull(),
  aggregateId: varchar("aggregate_id", { length: 32 }).notNull(),
  eventType: varchar("event_type", { length: 64 }).notNull(),
  topic: varchar("topic", { length: 128 }).notNull(),
  payload: json("payload").notNull(),
  status: varchar("status", { length: 16 })
    .notNull()
    .default("pending")
    .$type<OutboxStatus>(),
  retryCount: int("retry_count").notNull().default(0),
  maxRetryCount: int("max_retry_count").notNull().default(5),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  publishedAt: timestamp("published_at"),
  nextRetryAt: timestamp("next_retry_at"),
  lastError: text("last_error"),
  metadata: json("metadata").$type<Record<string, string> | null>(),
});

/** outbox 行类型 */
export type OutboxEvent = typeof outboxEvents.$inferSelect;
export type NewOutboxEvent = typeof outboxEvents.$inferInsert;

/**
 * processed_events 表：消费幂等（Redis 不可用时降级）。
 *
 * event_id 唯一索引：重复消费时 INSERT 冲突即跳过。
 */
export const processedEvents = mysqlTable("processed_events", {
  eventId: varchar("event_id", { length: 64 }).notNull().primaryKey(),
  topic: varchar("topic", { length: 128 }).notNull(),
  processedAt: timestamp("processed_at").notNull().defaultNow(),
});

export type ProcessedEvent = typeof processedEvents.$inferSelect;
export type NewProcessedEvent = typeof processedEvents.$inferInsert;
