/**
 * @ai-summary Controller for backup API endpoints
 * @ai-context Handles HTTP requests for backup operations
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { Pool } from 'pg';
import { logger } from '../../../core/logging/logger';
import { BackupService } from '../domain/backup.service';
import { BackupRestoreService } from '../domain/backup-restore.service';
import {
  ListBackupsQuery,
  CreateBackupBody,
  BackupIdParam,
  RestoreBody,
  CreateScheduleBody,
  UpdateScheduleBody,
  ScheduleIdParam,
  UpdateSettingsBody,
} from './backup.validation';
import { auditLogService } from '../../audit-log';

export class BackupController {
  private backupService: BackupService;
  private restoreService: BackupRestoreService;

  constructor(pool: Pool) {
    this.backupService = new BackupService(pool);
    this.restoreService = new BackupRestoreService(pool);
  }

  // ============================================
  // Backup Operations
  // ============================================

  async listBackups(
    request: FastifyRequest<{ Querystring: ListBackupsQuery }>,
    reply: FastifyReply
  ): Promise<void> {
    const result = await this.backupService.listBackups(request.query);
    reply.send(result);
  }

  async createBackup(
    request: FastifyRequest<{ Body: CreateBackupBody }>,
    reply: FastifyReply
  ): Promise<void> {
    const adminSub = (request as any).userContext?.auth0Sub;

    const result = await this.backupService.createBackup({
      name: request.body.name,
      backupType: 'manual',
      createdBy: adminSub,
      includeDocuments: request.body.includeDocuments,
    });

    if (result.success) {
      // Log backup creation to unified audit log
      await auditLogService.info(
        'system',
        adminSub || null,
        `Backup created: ${request.body.name || 'Manual backup'}`,
        'backup',
        result.backupId,
        { name: request.body.name, includeDocuments: request.body.includeDocuments }
      ).catch(err => logger.error('Failed to log backup create audit event', { error: err }));

      reply.status(201).send({
        backupId: result.backupId,
        status: 'completed',
        message: 'Backup created successfully',
      });
    } else {
      // Log backup failure
      await auditLogService.error(
        'system',
        adminSub || null,
        `Backup failed: ${request.body.name || 'Manual backup'}`,
        'backup',
        result.backupId,
        { error: result.error }
      ).catch(err => logger.error('Failed to log backup failure audit event', { error: err }));

      reply.status(500).send({
        backupId: result.backupId,
        status: 'failed',
        message: result.error,
      });
    }
  }

  async getBackup(
    request: FastifyRequest<{ Params: BackupIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    const backup = await this.backupService.getBackup(request.params.id);

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

    reply.send(backup);
  }

  async downloadBackup(
    request: FastifyRequest<{ Params: BackupIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    const download = await this.backupService.createDownloadStream(request.params.id);

    if (!download) {
      reply.status(404).send({ error: 'Backup not found or file not available' });
      return;
    }

    return reply
      .header('Content-Type', 'application/gzip')
      .header('Content-Disposition', `attachment; filename="${download.filename}"`)
      .header('Content-Length', download.size)
      .send(download.stream);
  }

  async deleteBackup(
    request: FastifyRequest<{ Params: BackupIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    const deleted = await this.backupService.deleteBackup(request.params.id);

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

    reply.status(204).send();
  }

  async uploadBackup(
    request: FastifyRequest,
    reply: FastifyReply
  ): Promise<void> {
    const adminSub = (request as any).userContext?.auth0Sub;

    // Handle multipart file upload
    const data = await request.file();
    if (!data) {
      reply.status(400).send({ error: 'No file uploaded' });
      return;
    }

    // Validate file type
    const filename = data.filename;
    if (!filename.endsWith('.tar.gz') && !filename.endsWith('.tgz')) {
      reply.status(400).send({ error: 'Invalid file type. Expected .tar.gz archive' });
      return;
    }

    // Save file temporarily
    const fs = await import('fs');
    const path = await import('path');
    const os = await import('os');

    const tempPath = path.join(os.tmpdir(), `upload-${Date.now()}-${filename}`);
    const writeStream = fs.createWriteStream(tempPath);

    await new Promise<void>((resolve, reject) => {
      data.file.pipe(writeStream);
      data.file.on('end', resolve);
      data.file.on('error', reject);
    });

    try {
      const backup = await this.backupService.importUploadedBackup(
        tempPath,
        filename,
        adminSub
      );

      reply.status(201).send({
        backupId: backup.id,
        filename: backup.filename,
        fileSizeBytes: backup.fileSizeBytes,
        message: 'Backup uploaded successfully',
      });
    } catch (error) {
      // Cleanup temp file on error
      await fs.promises.unlink(tempPath).catch(() => {});
      throw error;
    }
  }

  // ============================================
  // Restore Operations
  // ============================================

  async previewRestore(
    request: FastifyRequest<{ Params: BackupIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    try {
      const preview = await this.restoreService.previewRestore(request.params.id);
      reply.send(preview);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to preview restore';
      logger.error('Preview restore failed', {
        backupId: request.params.id,
        error: errorMessage,
        stack: error instanceof Error ? error.stack : undefined,
      });
      reply.status(400).send({
        error: errorMessage,
      });
    }
  }

  async executeRestore(
    request: FastifyRequest<{ Params: BackupIdParam; Body: RestoreBody }>,
    reply: FastifyReply
  ): Promise<void> {
    const adminSub = (request as any).userContext?.auth0Sub;

    try {
      const result = await this.restoreService.executeRestore({
        backupId: request.params.id,
        createSafetyBackup: request.body?.createSafetyBackup ?? true,
      });

      if (result.success) {
        // Log successful restore to unified audit log
        await auditLogService.info(
          'system',
          adminSub || null,
          `Backup restored: ${request.params.id}`,
          'backup',
          request.params.id,
          { safetyBackupId: result.safetyBackupId }
        ).catch(err => logger.error('Failed to log restore success audit event', { error: err }));

        reply.send({
          success: true,
          safetyBackupId: result.safetyBackupId,
          restoredAt: new Date().toISOString(),
          message: 'Restore completed successfully',
        });
      } else {
        // Log restore failure
        await auditLogService.error(
          'system',
          adminSub || null,
          `Backup restore failed: ${request.params.id}`,
          'backup',
          request.params.id,
          { error: result.error, safetyBackupId: result.safetyBackupId }
        ).catch(err => logger.error('Failed to log restore failure audit event', { error: err }));

        reply.status(500).send({
          success: false,
          safetyBackupId: result.safetyBackupId,
          error: result.error,
          message: 'Restore failed',
        });
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to execute restore';
      logger.error('Restore execution failed', {
        backupId: request.params.id,
        error: errorMessage,
        stack: error instanceof Error ? error.stack : undefined,
      });
      reply.status(400).send({
        success: false,
        error: errorMessage,
      });
    }
  }

  async getRestoreStatus(
    _request: FastifyRequest,
    reply: FastifyReply
  ): Promise<void> {
    const status = this.restoreService.getRestoreStatus();
    reply.send(status);
  }

  // ============================================
  // Schedule Operations
  // ============================================

  async listSchedules(
    _request: FastifyRequest,
    reply: FastifyReply
  ): Promise<void> {
    const schedules = await this.backupService.listSchedules();
    reply.send(schedules);
  }

  async createSchedule(
    request: FastifyRequest<{ Body: CreateScheduleBody }>,
    reply: FastifyReply
  ): Promise<void> {
    const schedule = await this.backupService.createSchedule(
      request.body.name,
      request.body.frequency,
      request.body.retentionCount,
      request.body.isEnabled
    );

    reply.status(201).send(schedule);
  }

  async updateSchedule(
    request: FastifyRequest<{ Params: ScheduleIdParam; Body: UpdateScheduleBody }>,
    reply: FastifyReply
  ): Promise<void> {
    const schedule = await this.backupService.updateSchedule(
      request.params.id,
      request.body
    );

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

    reply.send(schedule);
  }

  async deleteSchedule(
    request: FastifyRequest<{ Params: ScheduleIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    const deleted = await this.backupService.deleteSchedule(request.params.id);

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

    reply.status(204).send();
  }

  async toggleSchedule(
    request: FastifyRequest<{ Params: ScheduleIdParam }>,
    reply: FastifyReply
  ): Promise<void> {
    const schedule = await this.backupService.toggleSchedule(request.params.id);

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

    reply.send(schedule);
  }

  // ============================================
  // Settings Operations
  // ============================================

  async getSettings(
    _request: FastifyRequest,
    reply: FastifyReply
  ): Promise<void> {
    const settings = await this.backupService.getSettings();
    reply.send(settings);
  }

  async updateSettings(
    request: FastifyRequest<{ Body: UpdateSettingsBody }>,
    reply: FastifyReply
  ): Promise<void> {
    const settings = await this.backupService.updateSettings(request.body);
    reply.send(settings);
  }
}
