import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';
import { PrismaService } from '../../prisma/prisma.service';

interface AuditLogEntry {
  userId: string;
  clubId: string;
  action: string;
  resource: string;
  resourceId?: string;
  ipAddress: string;
  userAgent: string;
  statusCode: number;
  duration: number;
}

/**
 * AuditLogInterceptor records user actions to the audit_logs table.
 *
 * Captures: who (userId), what (action + resource), when (timestamp),
 * from where (IP, User-Agent), and result (status code, duration).
 */
@Injectable()
export class AuditLogInterceptor implements NestInterceptor {
  private readonly logger = new Logger(AuditLogInterceptor.name);

  constructor(private readonly prisma: PrismaService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    const startTime = Date.now();

    const user = request.user as { sub?: string; clubId?: string } | undefined;
    const method = request.method as string;
    const url = request.url as string;

    return next.handle().pipe(
      tap({
        next: () => {
          this.logAction({
            userId: user?.sub || 'anonymous',
            clubId: user?.clubId || 'unknown',
            action: method,
            resource: url,
            resourceId: request.params?.id,
            ipAddress: request.ip || request.connection?.remoteAddress || '',
            userAgent: request.headers?.['user-agent'] || '',
            statusCode: response.statusCode,
            duration: Date.now() - startTime,
          });
        },
        error: (error) => {
          this.logAction({
            userId: user?.sub || 'anonymous',
            clubId: user?.clubId || 'unknown',
            action: method,
            resource: url,
            resourceId: request.params?.id,
            ipAddress: request.ip || request.connection?.remoteAddress || '',
            userAgent: request.headers?.['user-agent'] || '',
            statusCode: error.status || 500,
            duration: Date.now() - startTime,
          });
        },
      }),
    );
  }

  private logAction(entry: AuditLogEntry): void {
    this.prisma
      .$executeRawUnsafe(
        `INSERT INTO audit_logs ("userId", "clubId", action, resource, "resourceId", "ipAddress", "userAgent", "statusCode", "durationMs", "createdAt")
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW())`,
        entry.userId,
        entry.clubId,
        entry.action,
        entry.resource,
        entry.resourceId || null,
        entry.ipAddress,
        entry.userAgent,
        entry.statusCode,
        entry.duration,
      )
      .catch((err) => {
        this.logger.warn(`Failed to write audit log: ${err.message}`);
      });
  }
}
