import {
  Controller,
  Get,
  Post,
  Delete,
  Param,
  Query,
  Res,
  Body,
  Logger,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
import type { Response } from 'express';

import { GdprService } from './gdpr.service';
import { DataRetentionService } from './data-retention.service';
import { DataRetentionCron } from './data-retention.cron';

/**
 * GDPR deletion request body
 */
interface DeletionRequestBody {
  requestedBy?: string;
  requestSource?: 'user' | 'admin' | 'automated';
  notes?: string;
}

/**
 * GdprController
 *
 * Implements GDPR compliance endpoints:
 * - Article 15: Right of Access (data export)
 * - Article 17: Right to Erasure (deletion)
 * - Article 20: Right to Data Portability
 *
 * Note: In production, these endpoints should be protected with authentication.
 * The generic @analytics product leaves auth to the deployer.
 */
@Controller('gdpr')
@ApiTags('GDPR')
export class GdprController {
  private readonly logger = new Logger(GdprController.name);

  constructor(
    private readonly gdprService: GdprService,
    private readonly dataRetentionService: DataRetentionService,
    private readonly dataRetentionCron: DataRetentionCron,
  ) {}

  /**
   * GET /gdpr/export/:subjectId
   *
   * Export all analytics data for a subject.
   */
  @Get('export/:subjectId')
  @ApiOperation({ summary: 'Export user data (GDPR Article 15)' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  @ApiQuery({ name: 'format', required: false, enum: ['json', 'readable'] })
  @ApiResponse({ status: 200, description: 'Data export successful' })
  async exportUserData(
    @Param('subjectId') subjectId: string,
    @Query('format') format?: 'json' | 'readable',
  ) {
    this.logger.log(`Data export requested for subject ${subjectId} (format: ${format ?? 'json'})`);

    const exportData = await this.gdprService.exportUserData(subjectId);

    if (format === 'readable') {
      return {
        format: 'readable',
        data: this.gdprService.formatAsReadable(exportData),
      };
    }

    return {
      format: 'json',
      data: exportData,
    };
  }

  /**
   * GET /gdpr/export/:subjectId/download
   *
   * Download user data as a file.
   */
  @Get('export/:subjectId/download')
  @ApiOperation({ summary: 'Download user data as file' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  @ApiQuery({ name: 'format', required: false, enum: ['json', 'txt'] })
  async downloadUserData(
    @Param('subjectId') subjectId: string,
    @Query('format') format: 'json' | 'txt' = 'json',
    @Res() res: Response,
  ) {
    this.logger.log(`Data download requested for subject ${subjectId} (format: ${format})`);

    const exportData = await this.gdprService.exportUserData(subjectId);

    switch (format) {
      case 'json':
        res.setHeader('Content-Type', 'application/json');
        res.setHeader(
          'Content-Disposition',
          `attachment; filename="analytics-export-${subjectId}-${Date.now()}.json"`,
        );
        res.send(this.gdprService.formatAsJson(exportData));
        break;

      case 'txt':
        res.setHeader('Content-Type', 'text/plain; charset=utf-8');
        res.setHeader(
          'Content-Disposition',
          `attachment; filename="analytics-export-${subjectId}-${Date.now()}.txt"`,
        );
        res.send(this.gdprService.formatAsReadable(exportData));
        break;
    }
  }

  /**
   * GET /gdpr/export/:subjectId/summary
   *
   * Get summary of what data exists for a subject.
   */
  @Get('export/:subjectId/summary')
  @ApiOperation({ summary: 'Get data summary for subject' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  async getExportSummary(@Param('subjectId') subjectId: string) {
    this.logger.log(`Data summary requested for subject ${subjectId}`);

    const exportData = await this.gdprService.exportUserData(subjectId);

    return {
      subjectId,
      statistics: exportData.statistics,
      hasData: exportData.metrics.length > 0,
      estimatedSizeKb: Math.round(
        JSON.stringify(exportData).length / 1024,
      ),
    };
  }

  /**
   * DELETE /gdpr/erase/:subjectId
   *
   * Request erasure of all user data.
   */
  @Delete('erase/:subjectId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Request data erasure (GDPR Article 17)' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  @ApiResponse({ status: 200, description: 'Deletion completed' })
  async requestDeletion(
    @Param('subjectId') subjectId: string,
    @Body() body: DeletionRequestBody,
  ) {
    this.logger.log(`Deletion requested for subject ${subjectId}`);

    const auditLog = await this.gdprService.requestDeletion(subjectId, body);

    return {
      success: auditLog.status === 'COMPLETED',
      auditLogId: auditLog.id,
      status: auditLog.status,
      recordsDeleted: auditLog.totalRecordsDeleted,
      recordsAnonymized: auditLog.totalRecordsAnonymized,
      completedAt: auditLog.completedAt,
    };
  }

  /**
   * GET /gdpr/erase/:subjectId/status
   *
   * Check deletion status for a subject.
   */
  @Get('erase/:subjectId/status')
  @ApiOperation({ summary: 'Check deletion status' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  async getDeletionStatus(@Param('subjectId') subjectId: string) {
    const status = await this.gdprService.getDeletionStatus(subjectId);

    if (!status) {
      return {
        subjectId,
        hasPendingDeletion: false,
        message: 'No deletion request found for this subject',
      };
    }

    return {
      subjectId,
      hasPendingDeletion: status.status === 'PENDING' || status.status === 'PROCESSING',
      status: status.status,
      requestedAt: status.requestedAt,
      completedAt: status.completedAt,
      recordsDeleted: status.totalRecordsDeleted,
      recordsAnonymized: status.totalRecordsAnonymized,
      verified: status.verified,
    };
  }

  /**
   * GET /gdpr/erase/:subjectId/verify
   *
   * Verify that all data has been deleted.
   */
  @Get('erase/:subjectId/verify')
  @ApiOperation({ summary: 'Verify deletion completed' })
  @ApiParam({ name: 'subjectId', description: 'Data subject ID' })
  async verifyDeletion(@Param('subjectId') subjectId: string) {
    const verification = await this.gdprService.verifyDeletion(subjectId);

    return {
      subjectId,
      verified: verification.verified,
      recordsFound: verification.recordsFound,
      tables: verification.tables,
    };
  }

  /**
   * GET /gdpr/retention/status
   *
   * Get data retention status and last execution summary.
   */
  @Get('retention/status')
  @ApiOperation({ summary: 'Get data retention status' })
  async getRetentionStatus() {
    return {
      lastExecution: this.dataRetentionCron.getLastExecutionSummary(),
      auditLogCount: this.dataRetentionCron.getAuditLog().length,
    };
  }

  /**
   * GET /gdpr/retention/audit
   *
   * Get retention operation audit log.
   */
  @Get('retention/audit')
  @ApiOperation({ summary: 'Get retention audit log' })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  async getRetentionAudit(@Query('limit') limit?: string) {
    const auditLog = this.dataRetentionCron.getAuditLog();
    const limitNum = limit ? parseInt(limit, 10) : 100;

    return {
      entries: auditLog.slice(0, limitNum),
      totalCount: auditLog.length,
    };
  }

  /**
   * POST /gdpr/retention/trigger
   *
   * Manually trigger data retention cleanup (admin only).
   */
  @Post('retention/trigger')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Trigger manual retention cleanup' })
  @ApiResponse({ status: 200, description: 'Cleanup triggered' })
  async triggerRetentionCleanup() {
    this.logger.log('Manual retention cleanup triggered via API');

    const results = await this.dataRetentionCron.triggerManualCleanup();

    const totalDeleted = results.reduce((sum, r) => sum + r.deletedCount, 0);
    const totalAnonymized = results.reduce(
      (sum, r) => sum + (r.anonymizedCount || 0),
      0,
    );

    return {
      success: true,
      results,
      summary: {
        totalDeleted,
        totalAnonymized,
        policiesExecuted: results.length,
      },
    };
  }

  /**
   * GET /gdpr/admin/audit-logs
   *
   * Get all deletion audit logs (admin only).
   */
  @Get('admin/audit-logs')
  @ApiOperation({ summary: 'Get all deletion audit logs' })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'offset', required: false, type: Number })
  async getAllAuditLogs(
    @Query('limit') limit?: string,
    @Query('offset') offset?: string,
  ) {
    const limitNum = limit ? parseInt(limit, 10) : 100;
    const offsetNum = offset ? parseInt(offset, 10) : 0;

    const logs = await this.gdprService.getAllDeletionLogs(limitNum, offsetNum);

    return {
      logs,
      pagination: {
        limit: limitNum,
        offset: offsetNum,
        count: logs.length,
      },
    };
  }
}
