import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import pino from 'pino';
import { withTenant } from '../data/db.js';
import { AgentBridge } from '../bridge/agent-bridge.js';
import { config } from '../config/index.js';

const logger = pino({ name: 'api-runbooks' });
const bridge = new AgentBridge(config.REDIS_URL);

const createRunbookSchema = z.object({
  name: z.string().min(1).max(200),
  description: z.string().max(2000).optional(),
  yaml_content: z.string().min(1),
});

const listQuerySchema = z.object({
  page: z.coerce.number().min(1).default(1),
  limit: z.coerce.number().min(1).max(100).default(20),
  status: z.enum(['active', 'archived']).optional(),
});

export function registerRunbookRoutes(app: FastifyInstance) {
  // List runbooks
  app.get('/api/v1/runbooks', async (req, reply) => {
    const query = listQuerySchema.parse(req.query);
    const tenantId = (req as any).tenantId;
    const offset = (query.page - 1) * query.limit;

    const result = await withTenant(tenantId, async (client) => {
      let sql = 'SELECT id, name, description, step_count, status, created_by, created_at, updated_at FROM runbooks WHERE 1=1';
      const params: any[] = [];
      let idx = 1;

      if (query.status) { sql += ` AND status = $${idx++}`; params.push(query.status); }
      sql += ` ORDER BY updated_at DESC LIMIT $${idx++} OFFSET $${idx++}`;
      params.push(query.limit, offset);

      const rows = await client.query(sql, params);
      const total = await client.query('SELECT COUNT(*)::int as count FROM runbooks' + (query.status ? ` WHERE status = '${query.status}'` : ''));
      return { rows: rows.rows, total: total.rows[0]?.count ?? 0 };
    });

    return { runbooks: result.rows, page: query.page, limit: query.limit, total: result.total };
  });

  // Get single runbook
  app.get('/api/v1/runbooks/:id', async (req, reply) => {
    const { id } = req.params as { id: string };
    const tenantId = (req as any).tenantId;

    const result = await withTenant(tenantId, async (client) => {
      return client.query('SELECT * FROM runbooks WHERE id = $1', [id]);
    });

    if (!result.rows[0]) return reply.status(404).send({ error: 'Runbook not found' });
    return { runbook: result.rows[0] };
  });

  // Create runbook
  app.post('/api/v1/runbooks', async (req, reply) => {
    const body = createRunbookSchema.parse(req.body);
    const tenantId = (req as any).tenantId;
    const userId = (req as any).userId;

    // Count steps in YAML (basic: count lines starting with "- description:" or "- command:")
    const stepCount = (body.yaml_content.match(/^\s*-\s+(description|command):/gm) ?? []).length;

    const result = await withTenant(tenantId, async (client) => {
      return client.query(
        `INSERT INTO runbooks (tenant_id, name, description, yaml_content, step_count, created_by)
         VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
        [tenantId, body.name, body.description ?? '', body.yaml_content, Math.max(1, Math.ceil(stepCount / 2)), userId],
      );
    });

    logger.info({ name: body.name, tenantId }, 'Runbook created');
    return reply.status(201).send({ runbook: result.rows[0] });
  });

  // Trigger runbook execution
  app.post('/api/v1/runbooks/:id/execute', async (req, reply) => {
    const { id } = req.params as { id: string };
    const tenantId = (req as any).tenantId;
    const userId = (req as any).userId;
    const body = z.object({
      dry_run: z.boolean().default(false),
      variables: z.record(z.string()).optional(),
    }).parse(req.body ?? {});

    const execution = await withTenant(tenantId, async (client) => {
      // Verify runbook exists
      const rb = await client.query('SELECT id, name FROM runbooks WHERE id = $1', [id]);
      if (!rb.rows[0]) return null;

      // Create execution record
      const exec = await client.query(
        `INSERT INTO executions (tenant_id, runbook_id, triggered_by, trigger_source, dry_run, variables)
         VALUES ($1, $2, $3, 'api', $4, $5) RETURNING *`,
        [tenantId, id, userId, body.dry_run, JSON.stringify(body.variables ?? {})],
      );

      return exec.rows[0];
    });

    if (!execution) return reply.status(404).send({ error: 'Runbook not found' });

    logger.info({ runbookId: id, executionId: execution.id, dryRun: body.dry_run }, 'Execution triggered');

    return reply.status(202).send({
      execution_id: execution.id,
      runbook_id: id,
      status: 'pending',
      dry_run: body.dry_run,
    });
  });

  // Get execution history for a runbook
  app.get('/api/v1/runbooks/:id/executions', async (req, reply) => {
    const { id } = req.params as { id: string };
    const tenantId = (req as any).tenantId;

    const result = await withTenant(tenantId, async (client) => {
      return client.query(
        `SELECT id, triggered_by, trigger_source, dry_run, status, started_at, completed_at, created_at
         FROM executions WHERE runbook_id = $1 ORDER BY created_at DESC LIMIT 50`,
        [id],
      );
    });

    return { executions: result.rows };
  });

  // Get execution detail with step-by-step audit trail
  app.get('/api/v1/executions/:executionId', async (req, reply) => {
    const { executionId } = req.params as { executionId: string };
    const tenantId = (req as any).tenantId;

    const result = await withTenant(tenantId, async (client) => {
      const execution = await client.query('SELECT * FROM executions WHERE id = $1', [executionId]);
      const steps = await client.query(
        `SELECT step_index, command, safety_level, status, approved_by, approval_method,
                exit_code, started_at, completed_at, duration_ms
         FROM audit_entries WHERE execution_id = $1 ORDER BY step_index`,
        [executionId],
      );
      return { execution: execution.rows[0] ?? null, steps: steps.rows };
    });

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