import { Injectable, inject } from '@angular/core';
import { Observable, throwError, map, catchError } from 'rxjs';
import { ApiService, validateId, validatePagination } from '../../../core/services/api.service';
import {
  RequestResponseDto,
  RequestDetailResponseDto,
  CreateRequestDto,
  UpdateRequestDto,
  PaginatedRequestsResponse,
  RequestFilters,
} from '../../../api/models';

/**
 * Validates and sanitizes request filters
 */
function sanitizeFilters(filters?: RequestFilters): 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 status
  const validStatuses = [
    'DRAFT',
    'SUBMITTED',
    'IN_REVIEW',
    'PENDING_RESUBMISSION',
    'APPROVED',
    'REJECTED',
    'REVOKED',
    'CANCELLED',
  ];
  if (filters.status && validStatuses.includes(filters.status)) {
    sanitized['status'] = filters.status;
  }

  // Validate request type
  const validTypes = ['NEW_LICENSE', 'RENEWAL', 'AMENDMENT', 'MODIFICATION', 'CANCELLATION'];
  if (filters.requestType && validTypes.includes(filters.requestType)) {
    sanitized['requestType'] = filters.requestType;
  }

  // Validate applicantId (if provided, must be non-empty string)
  if (filters.applicantId && typeof filters.applicantId === 'string') {
    const trimmed = filters.applicantId.trim();
    if (trimmed.length > 0) {
      sanitized['applicantId'] = trimmed;
    }
  }

  // Validate requestNumber (alphanumeric with dashes)
  if (filters.requestNumber && typeof filters.requestNumber === 'string') {
    const trimmed = filters.requestNumber.trim();
    if (trimmed.length > 0 && /^[a-zA-Z0-9-]+$/.test(trimmed)) {
      sanitized['requestNumber'] = trimmed;
    }
  }

  // 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;
  }

  // Validate sortBy
  const validSortFields = ['createdAt', 'updatedAt', 'requestNumber', 'status'];
  if (filters.sortBy && validSortFields.includes(filters.sortBy)) {
    sanitized['sortBy'] = filters.sortBy;
  }

  // Validate sortOrder
  if (filters.sortOrder && ['ASC', 'DESC'].includes(filters.sortOrder)) {
    sanitized['sortOrder'] = filters.sortOrder;
  }

  return sanitized;
}

/**
 * Ensures response has valid data array
 */
function ensureValidPaginatedResponse(
  response: PaginatedRequestsResponse | null | undefined,
  page: number,
  limit: number
): PaginatedRequestsResponse {
  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,
  };
}

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

  getRequests(filters?: RequestFilters): Observable<PaginatedRequestsResponse> {
    const sanitizedFilters = sanitizeFilters(filters);
    const page = (sanitizedFilters['page'] as number) || 1;
    const limit = (sanitizedFilters['limit'] as number) || 10;

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

  getRequest(id: string): Observable<RequestDetailResponseDto> {
    try {
      const validId = validateId(id, 'Request ID');
      return this.api.get<RequestDetailResponseDto>(`/requests/${validId}`).pipe(
        map((response) => {
          if (!response) {
            throw new Error('Request not found');
          }
          // Ensure nested arrays are never null/undefined
          return {
            ...response,
            documents: Array.isArray(response.documents) ? response.documents : [],
            approvals: Array.isArray(response.approvals) ? response.approvals : [],
            metadata: response.metadata ?? {},
          };
        }),
        catchError((error: unknown) => {
          const message = error instanceof Error ? error.message : `Failed to fetch request: ${id}`;
          return throwError(() => new Error(message));
        })
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  createRequest(dto: CreateRequestDto): Observable<RequestResponseDto> {
    if (!dto) {
      return throwError(() => new Error('Request data is required'));
    }

    // Validate required fields
    if (!dto.applicantName || typeof dto.applicantName !== 'string' || dto.applicantName.trim().length < 2) {
      return throwError(() => new Error('Applicant name must be at least 2 characters'));
    }

    if (!dto.requestType) {
      return throwError(() => new Error('Request type is required'));
    }

    // Either workflowId or workflowCode must be provided
    if ((!dto.workflowId || dto.workflowId.trim().length === 0) &&
        (!dto.workflowCode || dto.workflowCode.trim().length === 0)) {
      return throwError(() => new Error('Workflow ID or code is required'));
    }

    const sanitizedDto: CreateRequestDto = {
      applicantName: dto.applicantName.trim(),
      applicantPhone: dto.applicantPhone?.trim(),
      businessName: dto.businessName?.trim(),
      requestType: dto.requestType,
      workflowId: dto.workflowId?.trim(),
      workflowCode: dto.workflowCode?.trim(),
      metadata: dto.metadata ?? {},
      tokenId: dto.tokenId,
    };

    return this.api.post<RequestResponseDto>('/requests', sanitizedDto).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : 'Failed to create request';
        return throwError(() => new Error(message));
      })
    );
  }

  updateRequest(id: string, dto: UpdateRequestDto): Observable<RequestResponseDto> {
    try {
      const validId = validateId(id, 'Request ID');

      if (!dto) {
        return throwError(() => new Error('Update data is required'));
      }

      // Sanitize update data
      const sanitizedDto: UpdateRequestDto = {};

      if (dto.businessName !== undefined) {
        sanitizedDto.businessName =
          typeof dto.businessName === 'string' ? dto.businessName.trim() : undefined;
      }

      if (dto.description !== undefined) {
        sanitizedDto.description =
          typeof dto.description === 'string' ? dto.description.trim() : undefined;
      }

      if (dto.metadata !== undefined) {
        sanitizedDto.metadata = dto.metadata ?? {};
      }

      return this.api.patch<RequestResponseDto>(`/requests/${validId}`, sanitizedDto).pipe(
        catchError((error: unknown) => {
          const message = error instanceof Error ? error.message : `Failed to update request: ${id}`;
          return throwError(() => new Error(message));
        })
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  submitRequest(id: string): Observable<RequestResponseDto> {
    try {
      const validId = validateId(id, 'Request ID');
      return this.api.post<RequestResponseDto>(`/requests/${validId}/submit`, {}).pipe(
        catchError((error: unknown) => {
          const message = error instanceof Error ? error.message : `Failed to submit request: ${id}`;
          return throwError(() => new Error(message));
        })
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  cancelRequest(id: string): Observable<RequestResponseDto> {
    try {
      const validId = validateId(id, 'Request ID');
      return this.api.post<RequestResponseDto>(`/requests/${validId}/cancel`, {}).pipe(
        catchError((error: unknown) => {
          const message = error instanceof Error ? error.message : `Failed to cancel request: ${id}`;
          return throwError(() => new Error(message));
        })
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  deleteRequest(id: string): Observable<void> {
    try {
      const validId = validateId(id, 'Request ID');
      return this.api.delete<void>(`/requests/${validId}`).pipe(
        catchError((error: unknown) => {
          const message = error instanceof Error ? error.message : `Failed to delete request: ${id}`;
          return throwError(() => new Error(message));
        })
      );
    } catch (error) {
      return throwError(() => error);
    }
  }
}
