import { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { withTenant } from '../data/db.js';

const driftReportSchema = z.object({
  stack_name: z.string(),
  stack_fingerprint: z.string(),
  agent_version: z.string(),
  scanned_at: z.string(),
  state_serial: z.number(),
  lineage: z.string(),
  total_resources: z.number(),
  drifted_resources: z.array(z.object({
    resource_type: z.string(),
    resource_address: z.string(),
    module: z.string().optional(),
    provider: z.string(),
    severity: z.enum(['critical', 'high', 'medium', 'low']),
    diffs: z.array(z.object({
      attribute_name: z.string(),
      old_value: z.string(),
      new_value: z.string(),
      sensitive: z.boolean(),
    })),
  })),
  drift_score: z.number(),
  nonce: z.string(),
});

export async function registerProcessorRoutes(app: FastifyInstance) {
  app.post('/v1/ingest/drift', async (request, reply) => {
    const parsed = driftReportSchema.safeParse(request.body);
    if (!parsed.success) {
      return reply.status(400).send({ error: 'Invalid drift report', details: parsed.error.flatten() });
    }

    const report = parsed.data;
    const tenantId = (request as any).tenantId;

    // Nonce replay prevention
    const redis = (app as any).redis;
    const nonceKey = `nonce:${report.nonce}`;
    const exists = await redis.exists(nonceKey);
    if (exists) {
      return reply.status(409).send({ error: 'Nonce already used (replay rejected)' });
    }
    await redis.setex(nonceKey, 86400, '1'); // 24h TTL

    // Persist
    const pool = (app as any).pool;
    await withTenant(pool, tenantId, async (client) => {
      await client.query(
        `INSERT INTO drift_reports (tenant_id, stack_name, stack_fingerprint, agent_version, scanned_at, state_serial, lineage, total_resources, drift_score, nonce, raw_report)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
        [tenantId, report.stack_name, report.stack_fingerprint, report.agent_version, report.scanned_at, report.state_serial, report.lineage, report.total_resources, report.drift_score, report.nonce, JSON.stringify(report)]
      );
    });

    // Trigger notification if drift score exceeds threshold
    if (report.drift_score > 0) {
      try {
        const { dispatchNotifications, shouldNotify } = await import('../notifications/service.js');
        const { withTenant } = await import('../data/db.js');

        const maxSeverity = report.drifted_resources?.some((r: any) => r.severity === 'critical') ? 'critical'
          : report.drifted_resources?.some((r: any) => r.severity === 'high') ? 'high' : 'medium';

        // Fetch tenant notification channels
        const channelResult = await withTenant(pool, tenantId, async (client: any) => {
          return client.query('SELECT * FROM notification_channels WHERE enabled = true');
        });
        const channels = channelResult.rows.filter((ch: any) => shouldNotify(ch, report.drift_score, maxSeverity));

        if (channels.length > 0) {
          await dispatchNotifications(channels, {
            tenantId,
            stackName: report.stack_name,
            driftScore: report.drift_score,
            totalResources: report.total_resources,
            totalDrifted: report.drifted_resources?.length ?? 0,
            criticalCount: report.drifted_resources?.filter((r: any) => r.severity === 'critical').length ?? 0,
            highCount: report.drifted_resources?.filter((r: any) => r.severity === 'high').length ?? 0,
            reportUrl: `https://drift.dd0c.dev/reports/${report.stack_fingerprint}`,
          }, maxSeverity);
        }
      } catch (err) {
        app.log.warn({ tenantId, error: (err as Error).message }, 'Notification dispatch failed (non-fatal)');
      }
    }

    return reply.status(201).send({ status: 'accepted' });
  });
}
