import { type Path, type PathValue, type UseFormReturn } from 'react-hook-form';

import { format, isValid, parse } from 'date-fns';
import LZString from 'lz-string';
import { createParser } from 'nuqs';
import { type ZodObject } from 'zod';

import {
  type CoordinatorTaskFilters,
  type FilterFormValues,
  type OxxoCollaboratorType,
} from 'src/pages/dashboard/serviceManagement/types';

export const resetFormToDefaults = <T extends Record<string, unknown>>(
  form: UseFormReturn<T>,
  defaults: T,
) => {
  form.clearErrors();
  Object.entries(defaults).forEach(([key, value]) => {
    form.setValue(key as Path<T>, value as PathValue<T, Path<T>>);
  });
};

export function safeIsoDate(date: Date | null, start: boolean): string | null {
  if (!date) return null;

  try {
    // Copy the original - don't mutate the argument
    const dateCopy = new Date(date.getTime());

    if (start) {
      dateCopy.setHours(0, 0, 0, 0);
    } else {
      dateCopy.setHours(23, 59, 59, 999);
    }

    // Return the date in UTC format
    return dateCopy.toISOString();
  } catch {
    return null;
  }
}

export function getFilterParams(filters: FilterFormValues | null) {
  if (!filters) return {};

  return {
    statesIn: filters.statesIn.map(state => state.id).join(',') || null,
    catalogItemsIn:
      filters.serviceItemsIn.map(serviceItem => serviceItem.id).join(',') ||
      null,
    createdAtFrom: safeIsoDate(filters.creationDateFrom, true),
    createdAtTo: safeIsoDate(filters.creationDateTo, false),
    initiatorsIn:
      filters.initiatorsIn.map(initiator => initiator.id).join(',') || null,
    assigneesIn: filters.agentsIn.map(agent => agent.id).join(',') || null,
    initiatorSegmentations: parseSegmentations(
      filters.initiatorCollaboratorTypesIn,
    ),
  };
}

export function parseToValidURLDate(dateToParse: Date) {
  const formattedDateString = format(dateToParse, 'yyyy/MM/dd');
  return new Date(formattedDateString);
}

export const parseAsCustomDate = createParser<Date>({
  parse(queryValue) {
    const date = parse(queryValue, 'yyyy-MM-dd', new Date());
    if (!isValid(date)) return null;
    return date;
  },
  serialize(value) {
    return format(value, 'yyyy-MM-dd');
  },
});

// Custom parser for filter objects that handles date strings from JSON
// Serializes as compressed lz-string for shorter, non-readable URLs
// biome-ignore lint/suspicious/noExplicitAny: ZodObject generic params require any for flexible schema inference
export function createFilterParser<T>(schema: ZodObject<any, any, any, any>) {
  return createParser<T>({
    parse(queryValue) {
      try {
        // Try to decompress first (new format)
        const decompressed =
          LZString.decompressFromEncodedURIComponent(queryValue);
        // Fall back to raw JSON for backwards compatibility with old URLs
        const jsonString = decompressed ?? queryValue;
        const parsed = JSON.parse(jsonString);

        // Convert date strings to Date objects
        if (
          parsed.creationDateFrom &&
          typeof parsed.creationDateFrom === 'string'
        ) {
          parsed.creationDateFrom = new Date(parsed.creationDateFrom);
        }
        if (
          parsed.creationDateTo &&
          typeof parsed.creationDateTo === 'string'
        ) {
          parsed.creationDateTo = new Date(parsed.creationDateTo);
        }

        // Validate with schema
        const result = schema.parse(parsed);
        return result;
      } catch {
        return null;
      }
    },
    serialize(value) {
      const json = JSON.stringify(value);
      return LZString.compressToEncodedURIComponent(json);
    },
  });
}

export const trimFormValues = (values: unknown) => {
  if (typeof values !== 'object' || values === null) {
    return;
  }

  if (Array.isArray(values)) {
    values.forEach(trimFormValues);
    return;
  }

  for (const [key, value] of Object.entries(values)) {
    if (value === undefined) {
      delete (values as Record<string, unknown>)[key];
    } else if (typeof value === 'object' && value !== null) {
      trimFormValues(value);
    }
  }
};

export function parseSegmentations(
  initiatorSegmentations: OxxoCollaboratorType[] | null,
  encode = true,
) {
  if (!initiatorSegmentations || !initiatorSegmentations.length) return null;

  const groupId = 6151;
  const itemIds = initiatorSegmentations.map(type => type.id.toString());
  const segmentations = {
    [groupId]: itemIds,
  };

  const jsonString = JSON.stringify(segmentations);

  if (encode) {
    return encodeURIComponent(jsonString);
  }
  return jsonString;
}

export const parseAverageRating = (averageRating?: number | null) => {
  if (!averageRating) return '-';
  return averageRating.toFixed(2);
};

const SIX_MONTHS_IN_MS = 6 * 30 * 24 * 60 * 60 * 1000;

type DateFilterThresholdStatus = {
  exceeds: boolean;
  reason: 'from_only' | 'to_only' | 'no_dates' | 'range_too_large' | null;
};

export function getDateFilterThresholdStatus(
  localFilters: CoordinatorTaskFilters | FilterFormValues | null,
): DateFilterThresholdStatus {
  if (!localFilters) return { exceeds: true, reason: 'no_dates' };

  const dateOptions =
    'dateOptions' in localFilters ? localFilters.dateOptions : undefined;

  if (
    dateOptions?.lastSevenDays ||
    dateOptions?.lastFourteenDays ||
    dateOptions?.lastThirtyDays
  ) {
    return { exceeds: false, reason: null };
  }

  const creationDateFrom = localFilters.creationDateFrom ?? null;
  const creationDateTo = localFilters.creationDateTo ?? null;

  if (creationDateFrom && !creationDateTo) {
    const exceedsFromNow =
      Date.now() - new Date(creationDateFrom).getTime() > SIX_MONTHS_IN_MS;
    return {
      exceeds: exceedsFromNow,
      reason: exceedsFromNow ? 'from_only' : null,
    };
  }

  if (!creationDateFrom && creationDateTo) {
    return { exceeds: true, reason: 'to_only' };
  }

  if (!creationDateFrom && !creationDateTo) {
    return { exceeds: true, reason: 'no_dates' };
  }

  const from = new Date(creationDateFrom as Date);
  const to = new Date(creationDateTo as Date);
  const exceedsRange = to.getTime() - from.getTime() > SIX_MONTHS_IN_MS;

  return {
    exceeds: exceedsRange,
    reason: exceedsRange ? 'range_too_large' : null,
  };
}
