import { Injectable, OnModuleInit } from '@nestjs/common';
import { CloudEvent, IIOS_EVENTS } from '@insignia/iios-contracts';
import { PrismaService } from '../prisma/prisma.service';
import { OutboxBus } from '../outbox/outbox.bus';
import { DlqService } from '../outbox/dlq.service';
import { ProjectionCursorService } from '../projection/projection-cursor.service';

/**
 * P7→P8 auto-bridge (P8). When a P7 AI artifact is accepted and it carries an
 * EVENT claim, propose a meeting_request (PROPOSED) so the event surfaces as a
 * schedulable meeting — still human-confirmed before it becomes a meeting. AI
 * proposes, a human schedules. Idempotent via claim() and per-sourceClaim dedup.
 */
@Injectable()
export class CalendarProjector implements OnModuleInit {
  private readonly consumer = 'calendar-projector';

  constructor(
    private readonly prisma: PrismaService,
    private readonly bus: OutboxBus,
    private readonly dlq: DlqService,
    private readonly cursor: ProjectionCursorService,
  ) {}

  onModuleInit(): void {
    this.dlq.registerHandler(this.consumer, (e) => this.onArtifactAccepted(e));
    this.bus.on(IIOS_EVENTS.aiArtifactAccepted, (p) => void this.onArtifactAccepted(p as CloudEvent).catch((err) => this.dlq.onConsumerFailure(this.consumer, p as CloudEvent, err)));
  }

  async onArtifactAccepted(event: CloudEvent): Promise<void> {
    if (!(await this.claim(event.id))) return; // duplicate → no apply, no cursor advance
    await this.applyArtifactAccepted(event);
    await this.cursor.advance(this.consumer, event);
  }

  private async applyArtifactAccepted(event: CloudEvent): Promise<void> {
    const { artifactId } = event.data as { artifactId: string };
    const artifact = await this.prisma.iiosAiArtifact.findUnique({ where: { id: artifactId }, include: { job: true, claims: true } });
    if (!artifact?.acceptedByActorId) return;
    const eventClaims = artifact.claims.filter((c) => c.claimType === 'EVENT');
    for (const c of eventClaims) {
      const exists = await this.prisma.iiosMeetingRequest.count({ where: { sourceClaimId: c.id } });
      if (exists > 0) continue;
      const json = (c.claimJson ?? {}) as { when?: string };
      await this.prisma.iiosMeetingRequest.create({
        data: {
          scopeId: artifact.job.scopeId,
          interactionId: artifact.job.interactionId ?? undefined,
          requestedByActorId: artifact.acceptedByActorId,
          requestedWindowJson: { when: json.when ?? 'unspecified' },
          meetingType: 'INTERNAL',
          status: 'PROPOSED',
          sourceClaimId: c.id,
        },
      });
    }
  }

  private async claim(eventId: string): Promise<boolean> {
    const res = await this.prisma.iiosProcessedEvent.createMany({
      data: [{ consumerName: this.consumer, eventId }],
      skipDuplicates: true,
    });
    return res.count > 0;
  }
}
