/**
 * Audit Log Controller
 *
 * Provides endpoints for querying audit logs
 */

import {
  Controller,
  Get,
  Param,
  Query,
  UseGuards,
  ParseIntPipe,
  DefaultValuePipe,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery } from '@nestjs/swagger';
import { AuditService } from '../services/audit.service';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { RolesGuard } from '../guards/roles.guard';
import { Roles } from '../decorators/roles.decorator';
import { CurrentUser, UserPayload } from '../decorators/current-user.decorator';
import { AuditLog, AuditAction, AuditStatus } from '@domain/entities/audit-log.entity';

class AuditLogResponseDto {
  id: string;
  action: string;
  status: string;
  userId: string;
  userEmail: string;
  organizationId: string;
  resourceType?: string;
  resourceId?: string;
  resourceName?: string;
  metadata?: Record<string, any>;
  ipAddress?: string;
  userAgent?: string;
  errorMessage?: string;
  timestamp: string;
}

class _AuditLogQueryDto {
  userId?: string;
  action?: AuditAction[];
  status?: AuditStatus[];
  resourceType?: string;
  resourceId?: string;
  startDate?: string;
  endDate?: string;
  page?: number;
  limit?: number;
}

@ApiTags('Audit Logs')
@ApiBearerAuth()
@Controller('audit-logs')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AuditController {
  constructor(private readonly auditService: AuditService) {}

  /**
   * Get audit logs with filters
   * Only admins and managers can view audit logs
   */
  @Get()
  @Roles('admin', 'manager')
  @ApiOperation({ summary: 'Get audit logs with filters' })
  @ApiResponse({ status: 200, description: 'Audit logs retrieved successfully' })
  @ApiQuery({ name: 'userId', required: false, description: 'Filter by user ID' })
  @ApiQuery({
    name: 'action',
    required: false,
    description: 'Filter by action (comma-separated)',
    isArray: true,
  })
  @ApiQuery({
    name: 'status',
    required: false,
    description: 'Filter by status (comma-separated)',
    isArray: true,
  })
  @ApiQuery({ name: 'resourceType', required: false, description: 'Filter by resource type' })
  @ApiQuery({ name: 'resourceId', required: false, description: 'Filter by resource ID' })
  @ApiQuery({ name: 'startDate', required: false, description: 'Filter by start date (ISO 8601)' })
  @ApiQuery({ name: 'endDate', required: false, description: 'Filter by end date (ISO 8601)' })
  @ApiQuery({ name: 'page', required: false, description: 'Page number (default: 1)' })
  @ApiQuery({ name: 'limit', required: false, description: 'Items per page (default: 50)' })
  async getAuditLogs(
    @CurrentUser() user: UserPayload,
    @Query('userId') userId?: string,
    @Query('action') action?: string,
    @Query('status') status?: string,
    @Query('resourceType') resourceType?: string,
    @Query('resourceId') resourceId?: string,
    @Query('startDate') startDate?: string,
    @Query('endDate') endDate?: string,
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page?: number,
    @Query('limit', new DefaultValuePipe(50), ParseIntPipe) limit?: number
  ): Promise<{ logs: AuditLogResponseDto[]; total: number; page: number; pageSize: number }> {
    page = page || 1;
    limit = limit || 50;
    const filters: any = {
      organizationId: user.organizationId,
      userId,
      action: action ? action.split(',') : undefined,
      status: status ? status.split(',') : undefined,
      resourceType,
      resourceId,
      startDate: startDate ? new Date(startDate) : undefined,
      endDate: endDate ? new Date(endDate) : undefined,
      offset: (page - 1) * limit,
      limit,
    };

    const { logs, total } = await this.auditService.getAuditLogs(filters);

    return {
      logs: logs.map(log => this.mapToDto(log)),
      total,
      page,
      pageSize: limit,
    };
  }

  /**
   * Get specific audit log by ID
   */
  @Get(':id')
  @Roles('admin', 'manager')
  @ApiOperation({ summary: 'Get audit log by ID' })
  @ApiResponse({ status: 200, description: 'Audit log retrieved successfully' })
  @ApiResponse({ status: 404, description: 'Audit log not found' })
  async getAuditLogById(
    @Param('id') id: string,
    @CurrentUser() user: UserPayload
  ): Promise<AuditLogResponseDto> {
    const log = await this.auditService.getAuditLogs({
      organizationId: user.organizationId,
      limit: 1,
    });

    if (!log.logs.length) {
      throw new Error('Audit log not found');
    }

    return this.mapToDto(log.logs[0]);
  }

  /**
   * Get audit trail for a specific resource
   */
  @Get('resource/:type/:id')
  @Roles('admin', 'manager', 'user')
  @ApiOperation({ summary: 'Get audit trail for a specific resource' })
  @ApiResponse({ status: 200, description: 'Audit trail retrieved successfully' })
  async getResourceAuditTrail(
    @Param('type') resourceType: string,
    @Param('id') resourceId: string,
    @CurrentUser() user: UserPayload
  ): Promise<AuditLogResponseDto[]> {
    const logs = await this.auditService.getResourceAuditTrail(resourceType, resourceId);

    // Filter by organization for security
    const filteredLogs = logs.filter(log => log.organizationId === user.organizationId);

    return filteredLogs.map(log => this.mapToDto(log));
  }

  /**
   * Get recent activity for current organization
   */
  @Get('organization/activity')
  @Roles('admin', 'manager')
  @ApiOperation({ summary: 'Get recent organization activity' })
  @ApiResponse({ status: 200, description: 'Organization activity retrieved successfully' })
  @ApiQuery({ name: 'limit', required: false, description: 'Number of recent logs (default: 50)' })
  async getOrganizationActivity(
    @CurrentUser() user: UserPayload,
    @Query('limit', new DefaultValuePipe(50), ParseIntPipe) limit?: number
  ): Promise<AuditLogResponseDto[]> {
    limit = limit || 50;
    const logs = await this.auditService.getOrganizationActivity(user.organizationId, limit);
    return logs.map(log => this.mapToDto(log));
  }

  /**
   * Get user activity history
   */
  @Get('user/:userId/activity')
  @Roles('admin', 'manager')
  @ApiOperation({ summary: 'Get user activity history' })
  @ApiResponse({ status: 200, description: 'User activity retrieved successfully' })
  @ApiQuery({ name: 'limit', required: false, description: 'Number of recent logs (default: 50)' })
  async getUserActivity(
    @CurrentUser() user: UserPayload,
    @Param('userId') userId: string,
    @Query('limit', new DefaultValuePipe(50), ParseIntPipe) limit?: number
  ): Promise<AuditLogResponseDto[]> {
    limit = limit || 50;
    const logs = await this.auditService.getUserActivity(userId, limit);

    // Filter by organization for security
    const filteredLogs = logs.filter(log => log.organizationId === user.organizationId);

    return filteredLogs.map(log => this.mapToDto(log));
  }

  /**
   * Map domain entity to DTO
   */
  private mapToDto(log: AuditLog): AuditLogResponseDto {
    return {
      id: log.id,
      action: log.action,
      status: log.status,
      userId: log.userId,
      userEmail: log.userEmail,
      organizationId: log.organizationId,
      resourceType: log.resourceType,
      resourceId: log.resourceId,
      resourceName: log.resourceName,
      metadata: log.metadata,
      ipAddress: log.ipAddress,
      userAgent: log.userAgent,
      errorMessage: log.errorMessage,
      timestamp: log.timestamp.toISOString(),
    };
  }
}
