/**
 * Backup/Restore routes — backup CRUD, restore, config management.
 * @module backups/routes
 */

import type { FastifyInstance } from 'fastify';
import {
  UnauthorizedError,
  ForbiddenError,
  NotFoundError,
  BadRequestError,
} from '../../lib/errors.js';
import { getRequestProductId } from '../../lib/request-context.js';
import {
  CreateBackupSchema,
  RestoreBackupSchema,
  UpdateBackupConfigSchema,
  type BackupDoc,
  type RestoreDoc,
  type BackupConfigDoc,
  type BackupStatus,
} from './types.js';
import * as repo from './repository.js';

function requireAdmin(req: { jwtPayload?: { sub: string; role?: string } }): string {
  if (!req.jwtPayload?.sub) throw new UnauthorizedError('Authentication required');
  if (req.jwtPayload?.role !== 'admin') throw new ForbiddenError('Admin access required');
  return req.jwtPayload.sub;
}

export async function backupRoutes(app: FastifyInstance): Promise<void> {
  // ── Create backup ──────────────────────────────────────────
  app.post('/backups', async (req, reply) => {
    const userId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const input = CreateBackupSchema.parse(req.body);

    const now = new Date().toISOString();
    let expiresAt: string | null = null;
    if (input.expiresInDays) {
      const exp = new Date();
      exp.setDate(exp.getDate() + input.expiresInDays);
      expiresAt = exp.toISOString();
    }

    const doc: BackupDoc = {
      id: `bak_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
      productId,
      label: input.label,
      type: input.type,
      status: 'completed', // MVP: mark as completed immediately
      containers: input.containers ?? [],
      documentCount: 0, // Would be populated by actual backup logic
      sizeBytes: 0,
      storagePath: null,
      error: null,
      createdBy: userId,
      startedAt: now,
      completedAt: now,
      expiresAt,
    };

    const created = await repo.createBackup(doc);
    req.log.info({ backupId: created.id, type: input.type, userId }, 'Backup created');
    reply.status(201);
    return created;
  });

  // ── List backups ───────────────────────────────────────────
  app.get('/backups', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const { status, limit: limitStr } = req.query as { status?: string; limit?: string };
    const parsedLimit = limitStr ? parseInt(limitStr, 10) : 20;
    const safeLimit =
      Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, 100) : 20;

    return repo.listBackups(productId, {
      status: status as BackupStatus | undefined,
      limit: safeLimit,
    });
  });

  // ── Get backup ─────────────────────────────────────────────
  app.get<{ Params: { id: string } }>('/backups/:id', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const backup = await repo.getBackup(req.params.id, productId);
    if (!backup) throw new NotFoundError('Backup not found');
    return backup;
  });

  // ── Delete backup ──────────────────────────────────────────
  app.delete<{ Params: { id: string } }>('/backups/:id', async (req, reply) => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const deleted = await repo.deleteBackup(req.params.id, productId);
    if (!deleted) throw new NotFoundError('Backup not found');
    req.log.info({ backupId: req.params.id }, 'Backup deleted');
    reply.status(204);
    return;
  });

  // ── Restore from backup ────────────────────────────────────
  app.post('/backups/restore', async (req, reply) => {
    const userId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const input = RestoreBackupSchema.parse(req.body);

    const backup = await repo.getBackup(input.backupId, productId);
    if (!backup) throw new NotFoundError('Backup not found');
    if (backup.status !== 'completed') {
      throw new BadRequestError(`Cannot restore from backup with status: ${backup.status}`);
    }

    const now = new Date().toISOString();
    const doc: RestoreDoc = {
      id: `rst_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
      productId,
      backupId: input.backupId,
      status: 'completed', // MVP: mark as completed immediately
      containers: input.containers ?? backup.containers,
      documentCount: 0, // Would be populated by actual restore logic
      error: null,
      requestedBy: userId,
      startedAt: now,
      completedAt: now,
    };

    const created = await repo.createRestore(doc);
    req.log.info({ restoreId: created.id, backupId: input.backupId, userId }, 'Restore completed');
    reply.status(201);
    return created;
  });

  // ── List restores ──────────────────────────────────────────
  app.get('/backups/restores', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const { backupId, limit: limitStr } = req.query as { backupId?: string; limit?: string };
    const parsedLimit = limitStr ? parseInt(limitStr, 10) : 20;
    const safeLimit =
      Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, 100) : 20;

    return repo.listRestores(productId, { backupId, limit: safeLimit });
  });

  // ── Get restore ────────────────────────────────────────────
  app.get<{ Params: { id: string } }>('/backups/restores/:id', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const restore = await repo.getRestore(req.params.id, productId);
    if (!restore) throw new NotFoundError('Restore not found');
    return restore;
  });

  // ── Get backup config ──────────────────────────────────────
  app.get('/backups/config', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const config = await repo.getConfig(productId);

    if (!config) {
      return {
        productId,
        includedContainers: [],
        excludedContainers: [],
        scheduledBackupsEnabled: false,
        schedule: 'daily',
        retentionDays: 30,
        maxBackups: 10,
      };
    }

    return config;
  });

  // ── Update backup config ───────────────────────────────────
  app.put('/backups/config', async req => {
    const userId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const input = UpdateBackupConfigSchema.parse(req.body);

    const existing = await repo.getConfig(productId);
    const now = new Date().toISOString();

    const doc: BackupConfigDoc = {
      id: productId,
      productId,
      includedContainers: input.includedContainers ?? existing?.includedContainers ?? [],
      excludedContainers: input.excludedContainers ?? existing?.excludedContainers ?? [],
      scheduledBackupsEnabled:
        input.scheduledBackupsEnabled ?? existing?.scheduledBackupsEnabled ?? false,
      schedule: input.schedule ?? existing?.schedule ?? 'daily',
      retentionDays: input.retentionDays ?? existing?.retentionDays ?? 30,
      maxBackups: input.maxBackups ?? existing?.maxBackups ?? 10,
      updatedAt: now,
      updatedBy: userId,
    };

    const saved = await repo.upsertConfig(doc);
    req.log.info({ productId, userId }, 'Backup config updated');
    return saved;
  });
}
