import { BadRequestException, Controller, Get, Headers } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { ActorResolver, type MessagePrincipal } from '../identity/actor.resolver';
import { SessionVerifier } from '../platform/session.verifier';
import { ProjectionCursorService } from '../projection/projection-cursor.service';
import { RetentionService } from '../retention/retention.service';

/**
 * Tenant-scoped metrics (P9 observability, JSON). Returns the CALLER's tenant
 * counters (respecting the P9 isolation slice — no cross-tenant totals leak) plus a
 * global relay-lag section (non-tenant ops signal). A Prometheus text exposition +
 * real dashboards are a follow-up.
 */
@Controller('metrics')
export class MetricsController {
  constructor(
    private readonly prisma: PrismaService,
    private readonly actors: ActorResolver,
    private readonly session: SessionVerifier,
    private readonly cursors: ProjectionCursorService,
    private readonly retention: RetentionService,
  ) {}

  @Get()
  async metrics(@Headers('authorization') auth?: string) {
    const scope = await this.actors.findScope(this.principal(auth));
    const scopeId = scope?.id;

    const tenant = scopeId
      ? {
          interactions: await this.prisma.iiosInteraction.groupBy({ by: ['kind', 'status'], where: { scopeId }, _count: true }),
          outbound: await this.prisma.iiosOutboundCommand.groupBy({ by: ['status'], where: { scopeId }, _count: true }),
          aiJobs: await this.prisma.iiosAiJob.groupBy({ by: ['status'], where: { scopeId }, _count: true }),
          aiCostUnits: (await this.prisma.iiosAiModelRun.aggregate({ _sum: { costUnits: true }, where: { scopeId } }))._sum.costUnits ?? 0,
          tickets: await this.prisma.iiosTicket.groupBy({ by: ['state'], where: { scopeId }, _count: true }),
          meetings: await this.prisma.iiosMeeting.groupBy({ by: ['status'], where: { scopeId }, _count: true }),
          inbox: await this.prisma.iiosInboxItem.groupBy({ by: ['state'], where: { scopeId }, _count: true }),
          auditLinks: await this.prisma.iiosAuditLink.count({ where: { scopeId } }),
        }
      : null;

    // Global relay lag (ops signal, not tenant data).
    const outboxPending = await this.prisma.iiosOutboxEvent.count({ where: { status: { not: 'PUBLISHED' } } });
    const oldest = await this.prisma.iiosOutboxEvent.findFirst({ where: { status: { not: 'PUBLISHED' } }, orderBy: { createdAt: 'asc' } });
    const oldestPendingMs = oldest ? Date.now() - new Date(oldest.createdAt).getTime() : 0;

    // Projection cursors (global ops signal — position + determinism checksum + lag).
    const projections = (await this.cursors.list()).map((c) => ({
      projectionName: c.projectionName,
      sourceTopic: c.sourceTopic,
      partitionKey: c.partitionKey,
      lastEventId: c.lastEventId,
      lastOffset: c.lastOffset,
      checksum: c.checksum,
      lagMs: Date.now() - new Date(c.updatedAt).getTime(),
    }));

    return {
      ts: new Date().toISOString(),
      scope: scope ? { orgId: scope.orgId, appId: scope.appId, tenantId: scope.tenantId, cellId: scope.cellId } : null,
      tenant,
      relay: { outboxPending, oldestPendingMs },
      projections,
      retention: await this.retention.summary(),
    };
  }

  private principal(authorization?: string): MessagePrincipal {
    const token = (authorization ?? '').replace(/^Bearer\s+/i, '');
    if (!token) throw new BadRequestException('Authorization bearer token is required');
    return this.session.verify(token);
  }
}
