import { Kafka, type Consumer, type EachMessagePayload } from "kafkajs";
import { env } from "../../config/env.js";
import { logger } from "../observability/logger.js";
import { safeRedis } from "../cache/redis.client.js";
import { eventConsumedTotal } from "../observability/metrics.js";
import type { EventHandler } from "./handlers/event-handler.js";

/**
 * Kafka 消费者（对齐 02-architecture-design.md §5.3 + workline P5.4）。
 *
 * - consumer group: parent-bff-event-subscriber
 * - 订阅 7 个 topic（edu.notification.* + edu.teaching.*）
 * - 幂等性：Redis SETNX event_id 去重
 * - DLQ：edu.parent-bff.dlq
 * - 手动 commit offset
 *
 * KAFKA_BROKERS 未配置时不启动消费者（P4 阶段）。
 */

const IDEMPOTENCY_KEY_PREFIX = "bff:parent:event-id:";
const IDEMPOTENCY_TTL_SECONDS = 86400; // 24h
const DLQ_TOPIC = "edu.parent-bff.dlq";
const MAX_RETRIES = 3;

const SUBSCRIBED_TOPICS = [
  "edu.notification.sent",
  "edu.notification.read",
  "edu.notification.recalled",
  "edu.notification.failed",
  "edu.teaching.grade.recorded",
  "edu.teaching.homework.graded",
  "edu.teaching.exam.published",
];

export class KafkaEventConsumer {
  private consumer: Consumer | null = null;
  private readonly handlers = new Map<string, EventHandler[]>();

  constructor(
    private readonly kafka: Kafka,
    private readonly groupId: string,
  ) {}

  registerHandler(topic: string, handler: EventHandler): void {
    const existing = this.handlers.get(topic) ?? [];
    existing.push(handler);
    this.handlers.set(topic, existing);
    logger.info(
      { topic, groupId: this.groupId, handlerCount: existing.length },
      "Kafka handler registered",
    );
  }

  async start(): Promise<void> {
    if (!this.handlers.size) {
      logger.warn("No Kafka handlers registered, skipping consumer start");
      return;
    }

    this.consumer = this.kafka.consumer({
      groupId: this.groupId,
      sessionTimeout: 30000,
      rebalanceTimeout: 60000,
    });

    await this.consumer.connect();
    await this.consumer.subscribe({
      topics: SUBSCRIBED_TOPICS,
      fromBeginning: false,
    });

    await this.consumer.run({
      eachMessage: async (payload: EachMessagePayload) => {
        await this.handleMessage(payload);
      },
      autoCommit: false,
    });

    logger.info(
      { topics: SUBSCRIBED_TOPICS, groupId: this.groupId },
      "Kafka consumer started",
    );
  }

  async stop(): Promise<void> {
    if (this.consumer) {
      await this.consumer.disconnect();
      this.consumer = null;
      logger.info("Kafka consumer stopped");
    }
  }

  private async handleMessage(payload: EachMessagePayload): Promise<void> {
    const { topic, partition, message } = payload;
    const eventId =
      (message.headers?.["event_id"] as string) ??
      message.key?.toString() ??
      "";
    const key = message.key?.toString() ?? "";

    if (!eventId) {
      logger.warn(
        { topic, partition, offset: message.offset },
        "Event missing event_id, skipping",
      );
      return;
    }

    // 幂等性检查：Redis SETNX
    const idempotencyKey = `${IDEMPOTENCY_KEY_PREFIX}${eventId}`;
    const isDuplicate = await this.checkIdempotency(idempotencyKey);
    if (isDuplicate) {
      logger.debug({ eventId, topic }, "Duplicate event skipped (idempotency)");
      await this.consumer?.commitOffsets([
        { topic, partition, offset: (Number(message.offset) + 1).toString() },
      ]);
      return;
    }

    eventConsumedTotal.inc({ topic, event_type: key || "unknown" });

    const handlers = this.handlers.get(topic);
    if (!handlers || handlers.length === 0) {
      logger.warn({ topic }, "No handler registered for topic");
      await this.consumer?.commitOffsets([
        { topic, partition, offset: (Number(message.offset) + 1).toString() },
      ]);
      return;
    }

    const body = JSON.parse(message.value?.toString() ?? "{}");

    // 依次执行所有 handler（一个失败不影响其他）
    let lastError: Error | null = null;
    for (const handler of handlers) {
      let handlerError: Error | null = null;
      for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
        try {
          await handler.handle({ topic, eventId, key, body });
          handlerError = null;
          break;
        } catch (err) {
          handlerError = err instanceof Error ? err : new Error(String(err));
          logger.warn(
            { topic, eventId, attempt, error: handlerError.message },
            "Handler attempt failed",
          );
          if (attempt < MAX_RETRIES) {
            await this.sleep(Math.pow(2, attempt) * 1000);
          }
        }
      }
      if (handlerError) {
        lastError = handlerError;
      }
    }

    if (lastError) {
      logger.error(
        { topic, eventId, error: lastError.message },
        "Handler(s) failed after all retries, sending to DLQ",
      );
      await this.sendToDlq(
        topic,
        message.value ?? undefined,
        eventId,
        lastError,
      );
    }

    // 手动 commit
    await this.consumer?.commitOffsets([
      { topic, partition, offset: (Number(message.offset) + 1).toString() },
    ]);
  }

  private async checkIdempotency(key: string): Promise<boolean> {
    const result = await safeRedis(
      (client) => client.set(key, "1", "EX", IDEMPOTENCY_TTL_SECONDS, "NX"),
      null as string | null,
    );
    return result === "OK";
  }

  private async sendToDlq(
    originalTopic: string,
    originalValue: Buffer | undefined,
    eventId: string,
    error: Error,
  ): Promise<void> {
    if (!this.consumer) return;
    const producer = this.kafka.producer();
    try {
      await producer.connect();
      await producer.send({
        topic: DLQ_TOPIC,
        messages: [
          {
            key: eventId,
            value: JSON.stringify({
              originalTopic,
              eventId,
              error: error.message,
              timestamp: Date.now(),
              originalValue: originalValue?.toString(),
            }),
          },
        ],
      });
    } catch (err) {
      logger.error({ err, eventId }, "Failed to send message to DLQ");
    } finally {
      await producer.disconnect();
    }
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}

/**
 * 创建 Kafka 消费者实例（仅在 KAFKA_BROKERS 配置时）。
 */
export function createKafkaConsumer(): KafkaEventConsumer | null {
  if (!env.KAFKA_BROKERS) {
    logger.info("KAFKA_BROKERS not configured, Kafka consumer disabled");
    return null;
  }

  const brokers = env.KAFKA_BROKERS.split(",").map((b) => b.trim());
  const kafka = new Kafka({
    clientId: "parent-bff",
    brokers,
  });

  return new KafkaEventConsumer(kafka, env.KAFKA_CONSUMER_GROUP_ID);
}
