import type { FastifyInstance } from 'fastify';
import { requireAdmin } from '../../lib/auth.js';
import { createAuditLog } from '../audit/repository.js';
import { productId } from '../../lib/config.js';
import {
  runVmHealthCheck,
  getCleanupLog,
  runVmCleanup,
  getCronStatus,
  getUnhealthyContainers,
  restartContainer,
  getOllamaModels,
  unloadOllamaModel,
  getAllContainers,
  getContainerLogs,
} from './repository.js';
import {
  getDiskTrend,
  getMemoryTrend,
  getStealTrend,
  getIoTrend,
  getWeeklyDigestData,
} from './prometheus.js';
import { VmCleanupParamsSchema, VmContainerRestartParamsSchema } from './types.js';

// Best-effort audit-log helper for privileged shell-outs in this module.
// See `dashboard/DEPLOYMENT.md` Privilege Surface — every shell-exec route
// records action+entity+actor in Cosmos so a leaked admin token's actions
// stay reconstructable.
async function auditShellExec(
  fastify: FastifyInstance,
  req: { authUserId?: string; authRole?: string },
  entityId: string,
  details: Record<string, unknown>,
): Promise<void> {
  await createAuditLog({
    action: 'shell-exec',
    entityType: 'host',
    entityId,
    userId: req.authUserId ?? 'unknown',
    role: req.authRole ?? 'unknown',
    productId,
    details,
  }).catch((err) => fastify.log.warn({ err, entityId }, 'audit log write failed'));
}

export async function vmRoutes(fastify: FastifyInstance) {

  // ── Health check ──────────────────────────────────────────────────────────

  // GET /api/vm/health — run vm-health-check.sh --json
  fastify.get('/vm/health', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send(await runVmHealthCheck());
    } catch (error) {
      fastify.log.error(error, 'VM health check failed');
      return reply.code(500).send({ error: 'VM health check failed' });
    }
  });

  // ── Cleanup log (raw tail) ─────────────────────────────────────────────────

  // GET /api/vm/cleanup-log?lines=50
  fastify.get('/vm/cleanup-log', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    try {
      const lines = Math.min(Number((req.query as any).lines) || 50, 500);
      return reply.send({ log: await getCleanupLog(lines) });
    } catch (error) {
      fastify.log.error(error, 'Failed to read cleanup log');
      return reply.code(500).send({ error: 'Failed to read cleanup log' });
    }
  });

  // ── Cron status (parsed history + schedule) ───────────────────────────────

  // GET /api/vm/cron-status
  fastify.get('/vm/cron-status', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send(await getCronStatus());
    } catch (error) {
      fastify.log.error(error, 'Failed to get cron status');
      return reply.code(500).send({ error: 'Failed to get cron status' });
    }
  });

  // ── Cleanup trigger ───────────────────────────────────────────────────────

  // POST /api/vm/cleanup  { mode: 'weekly' | 'monthly' | 'dry-run' }
  fastify.post('/vm/cleanup', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    try {
      const params = VmCleanupParamsSchema.parse(req.body);
      const result = await runVmCleanup(params.mode);
      await auditShellExec(
        fastify,
        req as unknown as { authUserId?: string; authRole?: string },
        `vm-cleanup:${params.mode}`,
        { mode: params.mode },
      );
      return reply.send(result);
    } catch (error: any) {
      fastify.log.error(error, 'VM cleanup failed');
      return reply.code(500).send({ error: error.message || 'VM cleanup failed' });
    }
  });

  // ── All containers (full list with CPU/RAM) ───────────────────────────────

  // GET /api/vm/containers
  fastify.get('/vm/containers', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send(await getAllContainers());
    } catch (error) {
      fastify.log.error(error, 'Failed to get containers');
      return reply.code(500).send({ error: 'Failed to get containers' });
    }
  });

  // GET /api/vm/containers/:name/logs?lines=50
  fastify.get('/vm/containers/:name/logs', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    try {
      const { name } = VmContainerRestartParamsSchema.parse(req.params);
      const lines = Math.min(Number((req.query as any).lines) || 50, 200);
      return reply.send({ logs: await getContainerLogs(name, lines) });
    } catch (error: any) {
      fastify.log.error(error, 'Failed to get container logs');
      return reply.code(500).send({ error: error.message || 'Failed to get container logs' });
    }
  });

  // ── Unhealthy containers ──────────────────────────────────────────────────

  // GET /api/vm/containers/unhealthy
  fastify.get('/vm/containers/unhealthy', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send({ containers: await getUnhealthyContainers() });
    } catch (error) {
      fastify.log.error(error, 'Failed to get unhealthy containers');
      return reply.code(500).send({ error: 'Failed to get unhealthy containers' });
    }
  });

  // POST /api/vm/containers/:name/restart
  fastify.post('/vm/containers/:name/restart', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    try {
      const { name } = VmContainerRestartParamsSchema.parse(req.params);
      const result = await restartContainer(name);
      // Audit even when restart fails (e.g. invalid name) so attempted
      // privileged actions are still recorded.
      await auditShellExec(
        fastify,
        req as unknown as { authUserId?: string; authRole?: string },
        `container-restart:${name}`,
        { name, success: result.success, message: result.message },
      );
      return reply.code(result.success ? 200 : 400).send(result);
    } catch (error: any) {
      fastify.log.error(error, 'Container restart failed');
      return reply.code(500).send({ error: error.message || 'Container restart failed' });
    }
  });

  // ── Ollama / LLM models ───────────────────────────────────────────────────

  // GET /api/vm/ollama/models
  fastify.get('/vm/ollama/models', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send(await getOllamaModels());
    } catch (error) {
      fastify.log.error(error, 'Failed to get Ollama models');
      return reply.code(500).send({ error: 'Failed to get Ollama models' });
    }
  });

  // DELETE /api/vm/ollama/models/:name  — unload running model
  fastify.delete('/vm/ollama/models/:name', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    try {
      const name = decodeURIComponent((req.params as any).name ?? '');
      const result = await unloadOllamaModel(name);
      return reply.code(result.success ? 200 : 400).send(result);
    } catch (error: any) {
      fastify.log.error(error, 'Failed to unload Ollama model');
      return reply.code(500).send({ error: error.message || 'Unload failed' });
    }
  });

  // ── Prometheus trend queries ───────────────────────────────────────────────

  // GET /api/vm/metrics/trend?metric=disk|memory|steal|io&range=7d|30d
  fastify.get('/vm/metrics/trend', {
    preHandler: async (req) => requireAdmin(req),
  }, async (req, reply) => {
    const { metric = 'disk', range = '7d' } = req.query as Record<string, string>;
    const validRange = range === '30d' ? '30d' : '7d';
    try {
      switch (metric) {
        case 'disk':   return reply.send(await getDiskTrend(validRange));
        case 'steal':  return reply.send(await getStealTrend(validRange));
        case 'io':     return reply.send(await getIoTrend(validRange));
        case 'memory': return reply.send(await getMemoryTrend(validRange));
        default:
          return reply.code(400).send({ error: `Unknown metric: ${metric}` });
      }
    } catch (error: any) {
      fastify.log.error(error, 'Prometheus trend query failed');
      return reply.code(502).send({ error: 'Prometheus unavailable' });
    }
  });

  // GET /api/vm/weekly-digest — 7-day summary for Telegram digest
  fastify.get('/vm/weekly-digest', {
    preHandler: async (req) => requireAdmin(req),
  }, async (_req, reply) => {
    try {
      return reply.send(await getWeeklyDigestData());
    } catch (error: any) {
      fastify.log.error(error, 'Weekly digest data failed');
      return reply.code(502).send({ error: 'Prometheus unavailable' });
    }
  });
}
