import { WEB_SERVICE_PREFIX } from '@/constants/service';
import { request } from '../request';
import { type ProjectLocalDateValue, normalizeProjectLocalDate } from './project-shared';
import {
  type ServiceRequestResult,
  mapServiceResult,
  normalizeNullableStringId,
  normalizeStringId,
  safeJsonRequestConfig
} from './shared';

const OVERTIME_APPLICATION_PREFIX = `${WEB_SERVICE_PREFIX}/project/overtime-applications`;

type StringIdResponse = string | number;

type OvertimeApplicationResponse = Omit<
  Api.OvertimeApplication.OvertimeApplication,
  'id' | 'applicantId' | 'approverId' | 'overtimeDate' | 'allowEdit' | 'terminal'
> & {
  id: StringIdResponse;
  applicantId: StringIdResponse;
  approverId: StringIdResponse;
  overtimeDate: ProjectLocalDateValue;
  allowEdit?: boolean | number | string | null;
  terminal?: boolean | number | string | null;
};

type OvertimeApplicationPageResponse = Omit<Api.OvertimeApplication.OvertimeApplicationPageResult, 'total' | 'list'> & {
  total: number | string;
  list: OvertimeApplicationResponse[];
};

type OvertimeApplicationStatusLogResponse = Omit<
  Api.OvertimeApplication.OvertimeApplicationStatusLog,
  'id' | 'applicationId' | 'operatorUserId' | 'overtimeDateSnapshot'
> & {
  id: StringIdResponse;
  applicationId: StringIdResponse;
  operatorUserId: StringIdResponse;
  overtimeDateSnapshot: ProjectLocalDateValue;
};

function normalizeBooleanFlag(value: boolean | number | string | null | undefined) {
  if (typeof value === 'boolean') {
    return value;
  }

  if (typeof value === 'number') {
    return value === 1;
  }

  if (typeof value === 'string') {
    const normalized = value.trim().toLowerCase();

    return !['', '0', 'false', 'n', 'no'].includes(normalized);
  }

  return false;
}

function normalizeTotal(total: number | string) {
  const value = Number(total);

  return Number.isFinite(value) ? Math.max(0, value) : 0;
}

function normalizeOvertimeApplication(
  response: OvertimeApplicationResponse
): Api.OvertimeApplication.OvertimeApplication {
  return {
    ...response,
    id: normalizeStringId(response.id),
    applicantId: normalizeStringId(response.applicantId),
    approverId: normalizeStringId(response.approverId),
    overtimeDate: normalizeProjectLocalDate(response.overtimeDate) ?? '',
    statusName: response.statusName || response.statusCode,
    allowEdit: normalizeBooleanFlag(response.allowEdit),
    terminal: normalizeBooleanFlag(response.terminal),
    approvalComment: response.approvalComment ?? null,
    approvalTime: response.approvalTime ?? null
  };
}

function normalizeStatusLog(
  response: OvertimeApplicationStatusLogResponse
): Api.OvertimeApplication.OvertimeApplicationStatusLog {
  return {
    ...response,
    id: normalizeStringId(response.id),
    applicationId: normalizeStringId(response.applicationId),
    operatorUserId: normalizeStringId(response.operatorUserId),
    overtimeDateSnapshot: normalizeProjectLocalDate(response.overtimeDateSnapshot) ?? '',
    fromStatus: normalizeNullableStringId(response.fromStatus),
    reason: response.reason ?? null,
    remark: response.remark ?? null
  };
}

function createPageQuery(params: Api.OvertimeApplication.OvertimeApplicationSearchParams = {}) {
  const query = new URLSearchParams();

  query.append('pageNo', String(params.pageNo ?? 1));
  query.append('pageSize', String(params.pageSize ?? 10));

  if (params.keyword) {
    query.append('keyword', params.keyword);
  }

  if (params.applicantName) {
    query.append('applicantName', params.applicantName);
  }

  if (params.approverId) {
    query.append('approverId', params.approverId);
  }

  if (params.approverName) {
    query.append('approverName', params.approverName);
  }

  if (params.statusCode) {
    query.append('statusCode', params.statusCode);
  }

  params.overtimeDate?.forEach(item => {
    if (item) {
      query.append('overtimeDate', item);
    }
  });

  params.createTime?.forEach(item => {
    if (item) {
      query.append('createTime', item);
    }
  });

  return query.toString();
}

function toSaveRequest(data: Api.OvertimeApplication.SaveOvertimeApplicationParams) {
  return {
    overtimeDate: data.overtimeDate,
    overtimeDuration: data.overtimeDuration,
    overtimeReason: data.overtimeReason.trim(),
    overtimeContent: data.overtimeContent.trim(),
    approverId: data.approverId
  };
}

function toStatusActionRequest(data: Api.OvertimeApplication.StatusActionParams = {}) {
  return {
    reason: data.reason?.trim() || undefined
  };
}

export async function fetchGetOvertimeApplicationPage(
  params: Api.OvertimeApplication.OvertimeApplicationSearchParams = {}
) {
  const query = createPageQuery(params);

  const result = await request<OvertimeApplicationPageResponse>({
    ...safeJsonRequestConfig,
    url: query ? `${OVERTIME_APPLICATION_PREFIX}/page?${query}` : `${OVERTIME_APPLICATION_PREFIX}/page`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<OvertimeApplicationPageResponse>, data => ({
    total: normalizeTotal(data.total),
    list: data.list.map(normalizeOvertimeApplication)
  }));
}

export async function fetchGetOvertimeApplicationApprovalPage(
  params: Api.OvertimeApplication.OvertimeApplicationSearchParams = {}
) {
  const query = createPageQuery(params);

  const result = await request<OvertimeApplicationPageResponse>({
    ...safeJsonRequestConfig,
    url: query
      ? `${OVERTIME_APPLICATION_PREFIX}/approval-page?${query}`
      : `${OVERTIME_APPLICATION_PREFIX}/approval-page`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<OvertimeApplicationPageResponse>, data => ({
    total: normalizeTotal(data.total),
    list: data.list.map(normalizeOvertimeApplication)
  }));
}

export async function fetchGetOvertimeApplicationDetail(id: string) {
  const result = await request<OvertimeApplicationResponse>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<OvertimeApplicationResponse>, normalizeOvertimeApplication);
}

export async function fetchCreateOvertimeApplication(data: Api.OvertimeApplication.SaveOvertimeApplicationParams) {
  const result = await request<string | number>({
    ...safeJsonRequestConfig,
    url: OVERTIME_APPLICATION_PREFIX,
    method: 'post',
    data: toSaveRequest(data)
  });

  return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}

export function fetchUpdateRejectedOvertimeApplication(
  id: string,
  data: Api.OvertimeApplication.SaveOvertimeApplicationParams
) {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}`,
    method: 'put',
    data: toSaveRequest(data)
  });
}

export function fetchApproveOvertimeApplication(id: string, data: Api.OvertimeApplication.StatusActionParams = {}) {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}/approve`,
    method: 'post',
    data: toStatusActionRequest(data)
  });
}

export function fetchRejectOvertimeApplication(id: string, data: Api.OvertimeApplication.StatusActionParams) {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}/reject`,
    method: 'post',
    data: toStatusActionRequest(data)
  });
}

export function fetchCancelOvertimeApplication(id: string, data: Api.OvertimeApplication.StatusActionParams) {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}/cancel`,
    method: 'post',
    data: toStatusActionRequest(data)
  });
}

export function fetchDeleteOvertimeApplication(id: string) {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}`,
    method: 'delete'
  });
}

export async function fetchGetOvertimeApplicationStatusLogs(id: string) {
  const result = await request<OvertimeApplicationStatusLogResponse[]>({
    ...safeJsonRequestConfig,
    url: `${OVERTIME_APPLICATION_PREFIX}/${id}/status-logs`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<OvertimeApplicationStatusLogResponse[]>, data =>
    data.map(normalizeStatusLog)
  );
}

export function fetchExportOvertimeApplications(params: Api.OvertimeApplication.OvertimeApplicationSearchParams = {}) {
  const query = createPageQuery(params);

  return request<Blob, 'blob'>({
    url: query ? `${OVERTIME_APPLICATION_PREFIX}/export?${query}` : `${OVERTIME_APPLICATION_PREFIX}/export`,
    method: 'get',
    responseType: 'blob'
  });
}
