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

const submitReportSchema = z.object({
  stack_name: z.string().min(1).max(200),
  stack_fingerprint: z.string().min(1),
  agent_version: z.string().default('manual'),
  scanned_at: z.string().datetime().optional(),
  state_serial: z.number().int().min(0),
  lineage: z.string().default('manual'),
  total_resources: z.number().int().min(0),
  drift_score: z.number().min(0).max(100),
  raw_report: z.record(z.unknown()).default({}),
});

export async function registerApiRoutes(app: FastifyInstance) {
  // Submit a drift report (from agent or manual)
  app.post('/api/v1/reports', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const pool = (app as any).pool;
    const body = submitReportSchema.parse(request.body);
    const nonce = crypto.randomUUID();

    const result = await withTenant(pool, tenantId, async (client) => {
      const { rows } = 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)
         RETURNING id, stack_name, drift_score, scanned_at`,
        [tenantId, body.stack_name, body.stack_fingerprint, body.agent_version,
         body.scanned_at ?? new Date().toISOString(), body.state_serial, body.lineage,
         body.total_resources, body.drift_score, nonce, JSON.stringify(body.raw_report)]
      );
      return rows[0];
    });

    return reply.status(201).send({ report: result });
  });

  // Delete all reports for a stack
  app.delete('/api/v1/stacks/:stackName', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const { stackName } = request.params as { stackName: string };
    const pool = (app as any).pool;

    await withTenant(pool, tenantId, async (client) => {
      await client.query(
        'DELETE FROM drift_reports WHERE tenant_id = $1 AND stack_name = $2',
        [tenantId, stackName]
      );
    });

    return reply.status(200).send({ status: 'deleted', stack_name: stackName });
  });

  // List stacks
  app.get('/api/v1/stacks', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const pool = (app as any).pool;

    const result = await withTenant(pool, tenantId, async (client) => {
      const { rows } = await client.query(
        `SELECT DISTINCT ON (stack_name)
           stack_name, stack_fingerprint, drift_score, total_resources, scanned_at, state_serial
         FROM drift_reports
         WHERE tenant_id = $1
         ORDER BY stack_name, scanned_at DESC`,
        [tenantId]
      );
      return rows;
    });

    return reply.send(result);
  });

  // Get stack drift history
  app.get('/api/v1/stacks/:stackName/history', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const { stackName } = request.params as { stackName: string };
    const pool = (app as any).pool;

    const result = await withTenant(pool, tenantId, async (client) => {
      const { rows } = await client.query(
        `SELECT id, drift_score, total_resources, scanned_at, state_serial,
                (raw_report->'drifted_resources') as drifted_resources
         FROM drift_reports
         WHERE tenant_id = $1 AND stack_name = $2
         ORDER BY scanned_at DESC
         LIMIT 50`,
        [tenantId, stackName]
      );
      return rows;
    });

    return reply.send(result);
  });

  // Get single drift report
  app.get('/api/v1/reports/:reportId', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const { reportId } = request.params as { reportId: string };
    const pool = (app as any).pool;

    const result = await withTenant(pool, tenantId, async (client) => {
      const { rows } = await client.query(
        `SELECT * FROM drift_reports WHERE tenant_id = $1 AND id = $2`,
        [tenantId, reportId]
      );
      return rows[0] ?? null;
    });

    if (!result) {
      return reply.status(404).send({ error: 'Not found' });
    }

    return reply.send(result);
  });

  // Dashboard summary
  app.get('/api/v1/dashboard', async (request, reply) => {
    const tenantId = (request as any).tenantId;
    const pool = (app as any).pool;

    const result = await withTenant(pool, tenantId, async (client) => {
      const stacks = await client.query(
        `SELECT COUNT(DISTINCT stack_name) as stack_count FROM drift_reports WHERE tenant_id = $1`,
        [tenantId]
      );
      const drifted = await client.query(
        `SELECT COUNT(DISTINCT stack_name) as drifted_count
         FROM drift_reports
         WHERE tenant_id = $1 AND drift_score > 0
           AND scanned_at = (SELECT MAX(scanned_at) FROM drift_reports r2 WHERE r2.stack_name = drift_reports.stack_name AND r2.tenant_id = $1)`,
        [tenantId]
      );
      const critical = await client.query(
        `SELECT COUNT(*) as critical_count
         FROM drift_reports
         WHERE tenant_id = $1 AND drift_score >= 80
           AND scanned_at >= NOW() - INTERVAL '24 hours'`,
        [tenantId]
      );

      return {
        total_stacks: parseInt(stacks.rows[0]?.stack_count ?? '0'),
        drifted_stacks: parseInt(drifted.rows[0]?.drifted_count ?? '0'),
        critical_last_24h: parseInt(critical.rows[0]?.critical_count ?? '0'),
      };
    });

    return reply.send(result);
  });
}
