import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';

/**
 * Ensures DDL that the entity decorators cannot express exists before the
 * processor starts draining the queue.
 *
 * The aggregation upsert (aggregation.service.ts) relies on
 * `ON CONFLICT ("metricType","granularity","timestamp","dimension","dimensionValue")`,
 * which requires a unique index over exactly those columns that treats NULLs
 * as equal — global metrics key on NULL dimension/dimensionValue, so a plain
 * UNIQUE constraint (NULLs distinct) never conflicts and every upsert fails.
 *
 * Prod runs `synchronize: false` with no migration runner, so the `@Unique`
 * decorator on AggregatedMetric is never applied there — and even where it
 * is (dev sync), it lacks NULLS NOT DISTINCT. The 2026-05-16 → 2026-06-07
 * outage was exactly this: a fresh table without the index, every
 * aggregation failing for three weeks. This guard makes the fix survive
 * fresh deploys and new per-provider databases.
 *
 * Requires PostgreSQL 15+ (NULLS NOT DISTINCT).
 */
@Injectable()
export class SchemaGuardService implements OnModuleInit {
  private readonly logger = new Logger(SchemaGuardService.name);

  constructor(@InjectDataSource() private readonly dataSource: DataSource) {}

  async onModuleInit(): Promise<void> {
    // Pre-dedupe: legacy rows without the index can block CREATE UNIQUE INDEX.
    await this.dataSource.query(`
      DELETE FROM aggregated_metrics a
      USING aggregated_metrics b
      WHERE a.ctid < b.ctid
        AND a."metricType" = b."metricType"
        AND a."granularity" = b."granularity"
        AND a."timestamp" = b."timestamp"
        AND a."dimension" IS NOT DISTINCT FROM b."dimension"
        AND a."dimensionValue" IS NOT DISTINCT FROM b."dimensionValue"
    `);
    await this.dataSource.query(`
      CREATE UNIQUE INDEX IF NOT EXISTS uq_aggregated_metrics_dedup
      ON aggregated_metrics ("metricType", "granularity", "timestamp", "dimension", "dimensionValue")
      NULLS NOT DISTINCT
    `);
    this.logger.log('uq_aggregated_metrics_dedup ensured (NULLS NOT DISTINCT)');

    // session_fingerprints enrichment-column guard.
    //
    // The gov-detection + ASN fields were added to the SessionFingerprint entity
    // after the prod table was created. With `synchronize: false` and no migration
    // runner, those columns never reached prod — so every fingerprint INSERT threw
    // "column does not exist" and was swallowed by upsertSessionFingerprint's catch
    // (ingest.service.ts), silently freezing the table. raw_events kept filling, so
    // only the fingerprint-backed dashboard pages (Traffic/Audience/Network) went
    // blank. Same failure class as the aggregated_metrics outage above.
    //
    // All columns are nullable in the entity, so adding them is purely additive and
    // idempotent. `ALTER TABLE IF EXISTS` keeps this safe on a not-yet-created table.
    await this.dataSource.query(`
      ALTER TABLE IF EXISTS session_fingerprints
        ADD COLUMN IF NOT EXISTS "isGovernment" boolean,
        ADD COLUMN IF NOT EXISTS "orgType" varchar(30),
        ADD COLUMN IF NOT EXISTS "responseTier" varchar(20),
        ADD COLUMN IF NOT EXISTS "org" varchar(200),
        ADD COLUMN IF NOT EXISTS "asn" integer
    `);
    this.logger.log('session_fingerprints enrichment columns ensured (gov-detection + ASN)');
  }
}
