import { Injectable, inject } from '@angular/core';
import { Observable, map, catchError, throwError } from 'rxjs';
import { ApiService } from '../../../core/services/api.service';
import {
  DepartmentResponseDto,
  CreateDepartmentDto,
  UpdateDepartmentDto,
  PaginatedDepartmentsResponse,
  CreateDepartmentWithCredentialsResponse,
  RegenerateApiKeyResponse,
} from '../../../api/models';

interface ApiPaginatedResponse {
  data: DepartmentResponseDto[];
  meta: {
    total: number;
    page: number;
    limit: number;
    totalPages: number;
  };
}

const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

function validateId(id: string, fieldName = 'ID'): void {
  if (!id || typeof id !== 'string') {
    throw new Error(`${fieldName} is required and must be a string`);
  }
  const trimmedId = id.trim();
  if (trimmedId.length === 0) {
    throw new Error(`${fieldName} cannot be empty`);
  }
  if (!UUID_REGEX.test(trimmedId)) {
    throw new Error(`${fieldName} must be a valid UUID format`);
  }
}

function validateCode(code: string): void {
  if (!code || typeof code !== 'string') {
    throw new Error('Department code is required and must be a string');
  }
  const trimmedCode = code.trim();
  if (trimmedCode.length === 0) {
    throw new Error('Department code cannot be empty');
  }
}

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

  getDepartments(page = 1, limit = 10): Observable<PaginatedDepartmentsResponse> {
    if (page < 1) {
      return throwError(() => new Error('Page must be at least 1'));
    }
    if (limit < 1 || limit > 100) {
      return throwError(() => new Error('Limit must be between 1 and 100'));
    }

    return this.api.getRaw<ApiPaginatedResponse>('/departments', { page, limit }).pipe(
      map((response: ApiPaginatedResponse) => {
        const data = response?.data ?? [];
        const meta = response?.meta;
        return {
          data,
          total: meta?.total ?? data.length,
          page: meta?.page ?? page,
          limit: meta?.limit ?? limit,
          totalPages: meta?.totalPages ?? Math.ceil(data.length / limit),
          hasNextPage: (meta?.page ?? 1) < (meta?.totalPages ?? 1),
        };
      }),
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : 'Failed to fetch departments';
        return throwError(() => new Error(message));
      })
    );
  }

  getDepartment(id: string): Observable<DepartmentResponseDto> {
    try {
      validateId(id, 'Department ID');
    } catch (error) {
      return throwError(() => error);
    }

    return this.api.getRaw<DepartmentResponseDto>(`/departments/${id.trim()}`).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to fetch department with ID: ${id}`;
        return throwError(() => new Error(message));
      })
    );
  }

  getDepartmentByCode(code: string): Observable<DepartmentResponseDto> {
    try {
      validateCode(code);
    } catch (error) {
      return throwError(() => error);
    }

    return this.api.getRaw<DepartmentResponseDto>(`/departments/code/${encodeURIComponent(code.trim())}`).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to fetch department with code: ${code}`;
        return throwError(() => new Error(message));
      })
    );
  }

  createDepartment(dto: CreateDepartmentDto): Observable<CreateDepartmentWithCredentialsResponse> {
    if (!dto) {
      return throwError(() => new Error('Department data is required'));
    }

    return this.api.postRaw<CreateDepartmentWithCredentialsResponse>('/departments', dto).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : 'Failed to create department';
        return throwError(() => new Error(message));
      })
    );
  }

  updateDepartment(id: string, dto: UpdateDepartmentDto): Observable<DepartmentResponseDto> {
    try {
      validateId(id, 'Department ID');
    } catch (error) {
      return throwError(() => error);
    }

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

    return this.api.patch<DepartmentResponseDto>(`/departments/${id.trim()}`, dto).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to update department with ID: ${id}`;
        return throwError(() => new Error(message));
      })
    );
  }

  deleteDepartment(id: string): Observable<void> {
    try {
      validateId(id, 'Department ID');
    } catch (error) {
      return throwError(() => error);
    }

    return this.api.delete<void>(`/departments/${id.trim()}`).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to delete department with ID: ${id}`;
        return throwError(() => new Error(message));
      })
    );
  }

  regenerateApiKey(id: string): Observable<RegenerateApiKeyResponse> {
    try {
      validateId(id, 'Department ID');
    } catch (error) {
      return throwError(() => error);
    }

    return this.api.post<RegenerateApiKeyResponse>(`/departments/${id.trim()}/regenerate-key`, {}).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to regenerate API key for department: ${id}`;
        return throwError(() => new Error(message));
      })
    );
  }

  toggleActive(id: string, isActive: boolean): Observable<DepartmentResponseDto> {
    try {
      validateId(id, 'Department ID');
    } catch (error) {
      return throwError(() => error);
    }

    if (typeof isActive !== 'boolean') {
      return throwError(() => new Error('isActive must be a boolean value'));
    }

    return this.api.patch<DepartmentResponseDto>(`/departments/${id.trim()}`, { isActive }).pipe(
      catchError((error: unknown) => {
        const message = error instanceof Error ? error.message : `Failed to toggle active status for department: ${id}`;
        return throwError(() => new Error(message));
      })
    );
  }
}
