import { Injectable, inject } from '@angular/core';
import { Observable, throwError, map, catchError } from 'rxjs';
import { ApiService, validateId, validatePagination } from '../../../core/services/api.service';
import {
  AuditLogDto,
  EntityAuditTrailDto,
  AuditMetadataDto,
  PaginatedAuditLogsResponse,
  AuditLogFilters,
} from '../../../api/models';

/**
 * Validates and sanitizes audit log filters
 */
function sanitizeAuditFilters(filters?: AuditLogFilters): Record<string, string | number | boolean> {
  if (!filters) {
    return {};
  }

  const sanitized: Record<string, string | number | boolean> = {};

  // Validate pagination
  const { page, limit } = validatePagination(filters.page, filters.limit);
  sanitized['page'] = page;
  sanitized['limit'] = limit;

  // Validate entity type (alphanumeric with underscores)
  if (filters.entityType && typeof filters.entityType === 'string') {
    const trimmed = filters.entityType.trim();
    if (trimmed.length > 0 && /^[A-Z_]+$/.test(trimmed)) {
      sanitized['entityType'] = trimmed;
    }
  }

  // Validate entityId
  if (filters.entityId && typeof filters.entityId === 'string') {
    const trimmed = filters.entityId.trim();
    if (trimmed.length > 0 && !/[<>|"'`;&$]/.test(trimmed)) {
      sanitized['entityId'] = trimmed;
    }
  }

  // Validate action
  const validActions = ['CREATE', 'UPDATE', 'DELETE', 'APPROVE', 'REJECT', 'SUBMIT', 'CANCEL', 'DOWNLOAD'];
  if (filters.action && validActions.includes(filters.action)) {
    sanitized['action'] = filters.action;
  }

  // Validate actorId
  if (filters.actorId && typeof filters.actorId === 'string') {
    const trimmed = filters.actorId.trim();
    if (trimmed.length > 0 && !/[<>|"'`;&$]/.test(trimmed)) {
      sanitized['actorId'] = trimmed;
    }
  }

  // Validate actorType
  const validActorTypes = ['APPLICANT', 'DEPARTMENT', 'SYSTEM', 'ADMIN'];
  if (filters.actorType && validActorTypes.includes(filters.actorType)) {
    sanitized['actorType'] = filters.actorType;
  }

  // Validate dates (ISO format)
  const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/;
  if (filters.startDate && isoDateRegex.test(filters.startDate)) {
    sanitized['startDate'] = filters.startDate;
  }
  if (filters.endDate && isoDateRegex.test(filters.endDate)) {
    sanitized['endDate'] = filters.endDate;
  }

  return sanitized;
}

/**
 * Ensures paginated response is valid
 */
function ensureValidPaginatedResponse(
  response: PaginatedAuditLogsResponse | null | undefined,
  page: number,
  limit: number
): PaginatedAuditLogsResponse {
  if (!response) {
    return {
      data: [],
      total: 0,
      page,
      limit,
      totalPages: 0,
      hasNextPage: false,
    };
  }

  return {
    data: Array.isArray(response.data) ? response.data : [],
    total: typeof response.total === 'number' && response.total >= 0 ? response.total : 0,
    page: typeof response.page === 'number' && response.page >= 1 ? response.page : page,
    limit: typeof response.limit === 'number' && response.limit >= 1 ? response.limit : limit,
    totalPages: typeof response.totalPages === 'number' && response.totalPages >= 0 ? response.totalPages : 0,
    hasNextPage: typeof response.hasNextPage === 'boolean' ? response.hasNextPage : false,
  };
}

/**
 * Validates entity type for trail lookup
 */
function validateEntityType(entityType: string | undefined | null): string {
  if (!entityType || typeof entityType !== 'string') {
    throw new Error('Entity type is required');
  }

  const trimmed = entityType.trim();

  if (trimmed.length === 0) {
    throw new Error('Entity type cannot be empty');
  }

  // Entity types should be uppercase with underscores (e.g., REQUEST, DOCUMENT, USER)
  if (!/^[A-Z][A-Z_]*$/.test(trimmed)) {
    throw new Error('Entity type must be uppercase letters and underscores');
  }

  return trimmed;
}

@Injectable({
  providedIn: 'root',
})
export class AuditService {
  private readonly api = inject(ApiService);

  getAuditLogs(filters?: AuditLogFilters): Observable<PaginatedAuditLogsResponse> {
    const sanitizedFilters = sanitizeAuditFilters(filters);
    const page = (sanitizedFilters['page'] as number) || 1;
    const limit = (sanitizedFilters['limit'] as number) || 10;

    return this.api.get<PaginatedAuditLogsResponse>('/audit', sanitizedFilters).pipe(
      map((response) => ensureValidPaginatedResponse(response, page, limit)),
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : 'Failed to fetch audit logs';
        return throwError(() => new Error(message));
      })
    );
  }

  getEntityTrail(entityType: string, entityId: string): Observable<EntityAuditTrailDto> {
    try {
      const validEntityType = validateEntityType(entityType);
      const validEntityId = validateId(entityId, 'Entity ID');

      return this.api
        .get<EntityAuditTrailDto>(`/audit/entity/${encodeURIComponent(validEntityType)}/${validEntityId}`)
        .pipe(
          map((response) => {
            if (!response) {
              return {
                entityId: validEntityId,
                entityType: validEntityType,
                events: [],
              };
            }
            return {
              entityId: response.entityId || validEntityId,
              entityType: response.entityType || validEntityType,
              events: Array.isArray(response.events) ? response.events : [],
            };
          }),
          catchError((error: unknown) => {
            const message =
              error instanceof Error
                ? error.message
                : `Failed to fetch audit trail for ${entityType}:${entityId}`;
            return throwError(() => new Error(message));
          })
        );
    } catch (error) {
      return throwError(() => error);
    }
  }

  getAuditMetadata(): Observable<AuditMetadataDto> {
    return this.api.get<AuditMetadataDto>('/audit/metadata').pipe(
      map((response) => {
        if (!response) {
          return {
            actions: [],
            entityTypes: [],
            actorTypes: [],
          };
        }
        return {
          actions: Array.isArray(response.actions) ? response.actions : [],
          entityTypes: Array.isArray(response.entityTypes) ? response.entityTypes : [],
          actorTypes: Array.isArray(response.actorTypes) ? response.actorTypes : [],
        };
      }),
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : 'Failed to fetch audit metadata';
        return throwError(() => new Error(message));
      })
    );
  }
}
