import { Timestamp, TimestampsAction } from '@win2win/shared';
import { camelCase, chunk, first, isArray, isNil, last, mapKeys, orderBy, snakeCase, sum, upperFirst } from 'lodash';
import moment from 'moment';
import { AppLocationLocale, ContactoLite, Ubicacion } from 'src/models';

const formatOneWord = (word: string) => word.charAt(0).toUpperCase() + word.slice(1);

export const capitalize = (text: string, allWords = false) => {
  if (typeof text !== 'string') return text;
  text = text.toLowerCase().replaceAll('_', ' ').trim();
  return allWords ? text.split(' ').map(formatOneWord).join(' ') : formatOneWord(text);
};

export const formatDate = (date?: Date | number | string, showTime = false) => {
  if (!date) {
    return '~';
  }
  return moment(date).format('DD/MM/YYYY' + (showTime ? ' HH:mm' : ''));
};

export const secondsToTime = (seconds?: number | string, includeHours = false) => {
  if (isNil(seconds) || isNaN(Number(seconds))) return '';
  const duration = moment.duration(Number(seconds), 'seconds');
  const hours = Math.floor(duration.asHours());
  const minutes = Math.floor(duration.asMinutes());
  const secs = duration.seconds();
  return `${includeHours ? `${hours.toString().padStart(2, '0')}:` : ''}${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};

export const addSpaceAfterComma = (inputString) => {
  let result = '';
  for (let i = 0; i < inputString.length; i++) {
    result += inputString[i];
    if (inputString[i].toLowerCase() === ',') {
      result += ' ';
    }
  }
  return result;
};

export const toEUR = (val?: number | null | string, decimals?: number, symbol: string = 'EUR') => {
  const onlyNumbers = val?.toString().replace(/[^0-9.,]/g, '') || 0;
  return Number(onlyNumbers).toLocaleString('de-DE', { style: 'currency', currency: symbol, minimumFractionDigits: decimals ?? 2, maximumFractionDigits: decimals ?? 2 });
};

export const toMXN = (value: number) => {
  const formatted = new Intl.NumberFormat('es-MX', {
    style: 'currency',
    currency: 'MXN',
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(value);
  return `${formatted} MX`;
};

export const toUSD = (val?: number | null | string, decimals?: number, symbol: string = 'USD') => {
  const onlyNumbers = val?.toString().replace(/[^0-9.,]/g, '') || 0;
  return Number(onlyNumbers).toLocaleString('en-US', { style: 'currency', currency: symbol, minimumFractionDigits: decimals ?? 2, maximumFractionDigits: decimals ?? 2 });
};

export const toPercentage = (val: number | string, decimals = 2) => {
  const value = Number(val || 0);
  if (isNaN(value)) val = 0;
  return value.toFixed(decimals) + ' %';
};

export function formatParameters(items) {
  return items
    .filter((item) => item.ESTADO == 1)
    .map((item) => {
      return {
        value: item.CLAVE,
        label: `${item.CLAVE} - ${item.VALOR}`,
      };
    });
}

export function formatToDateTime(dateString: string): string {
  return moment(dateString, 'DD/MM/YYYY').format('YYYY-MM-DD HH:mm:ss');
}

export function formatToDDMMYYYY(dateTimeString: string): string {
  // Extract the date part (YYYY-MM-DD) from the datetime string
  const [date] = dateTimeString.split(' '); // Ignore the time part
  const [year, month, day] = date.split('-').map(Number);

  // Return the formatted string in DD/MM/YYYY format
  return `${day.toString().padStart(2, '0')}/${month.toString().padStart(2, '0')}/${year}`;
}

export const formatContactLiteName = (contact: ContactoLite) => capitalize(`${contact.nombre || ''} ${contact.apellido || ''} ${contact.apellido2 || ''}`, true);

export const formatContactName = (contact?: { NOMBRE?: string | null; APELLIDO?: string | null; APELLIDO2?: string | null } | null) => {
  if (!contact) return '';
  return capitalize(`${contact.NOMBRE || ''} ${contact.APELLIDO || ''} ${contact.APELLIDO2 || ''}`, true).trim();
};

export const upperCamelCase = (text?: string) => upperFirst(camelCase(String(text ?? '')));

export const upperSnakeCase = (text?: string) => snakeCase(text).toUpperCase();

export const upperSnakeKeys = (item: Record<string, any>) => mapKeys(item, (_, k) => upperSnakeCase(k));

export const toAppLocation = (locationLocale: AppLocationLocale) => {
  return {
    registry: locationLocale?.catastro || '',
    street: locationLocale?.calle || '',
    streetNumber: locationLocale?.numero?.toString() || '',
    city: locationLocale?.ciudad || '',
    zone: locationLocale?.provincia || '',
    country: locationLocale?.pais || '',
    postalCode: locationLocale?.cod_postal || '',
    town: locationLocale?.municipio || '',
    colony: locationLocale?.colonia || '',
    street_between_1: locationLocale?.entre_calle || '',
    street_between_2: locationLocale?.entre_calle2 || '',
    fullAddress: '',
    geolocation: {
      lat: locationLocale?.latitude || 41.4320267,
      lng: locationLocale?.longitude || 2.1693445,
    },
  };
};

export const formatLocation = (location?: Partial<Ubicacion>, fields: string[] = ['CIUDAD', 'PROVINCIA']) => {
  if (!location) return '~';
  return (
    fields
      .map((field) => String(location[field] || '').trim())
      .filter(Boolean)
      .join(', ') || '~'
  );
};

export function getFirstTimestamp(data: Timestamp[], action = null, format = 'HH:mm:ss DD/MM/YYYY') {
  if (!data?.length) return '';
  const timestamps = data.filter((item) => (action !== null ? item.ACTION == action : true)).map((item) => new Date(item.TIMESTAMP));
  if (!timestamps.length) return '';
  const item = first(orderBy(timestamps, (v) => v.getTime(), 'asc'));
  return moment(item).format(format);
}

export function getLastTimestamp(data: Timestamp[], action = null, format = 'HH:mm:ss DD/MM/YYYY') {
  if (!data?.length) return '';
  const timestamps = data.filter((item) => (action !== null ? item.ACTION == action : true)).map((item) => new Date(item.TIMESTAMP));
  if (!timestamps.length) return '';
  const item = last(orderBy(timestamps, (v) => v.getTime(), 'asc'));
  return moment(item).format(format);
}

export function getTimestampsGroups(data: Timestamp[]) {
  const sortedTimestamps = orderBy(data, (item) => new Date(item.TIMESTAMP).getTime(), 'asc');
  if (first(sortedTimestamps)?.ACTION !== TimestampsAction.CHECKIN) return [];
  const cleanTimestamps = sortedTimestamps.filter((item, index) => item.ACTION !== sortedTimestamps[index + 1]?.ACTION);
  return chunk(cleanTimestamps, 2) as [Timestamp, Timestamp][];
}

export function getDuration(interval: [Timestamp, Timestamp], granularity: 'seconds' | 'minutes' | 'hours' | 'days' = 'minutes') {
  if (!interval || !interval.length) return 0;

  // si no hay end se toma el momento actual, aunque no siempre será el caso
  const [start, end = moment()] = interval.map((item) => moment(item.TIMESTAMP));
  const duration = end.diff(start, granularity);
  return duration;
}

export function getTotalDuration(timestamps: Timestamp[], format?: string) {
  const groups = getTimestampsGroups(timestamps);
  const totalDuration = sum(groups.map((group) => getDuration(group, 'minutes')));
  const durationAsDate = moment.duration(totalDuration, 'minutes').asMilliseconds();
  if (!format) return durationAsDate;
  // vamos a reemplazar HH por la cantidad total de horas ya sea de un día o varios
  if (format.includes('HH')) {
    const totalHours = Math.floor(totalDuration / 60);
    format = format.replace('HH', totalHours.toString());
  }

  return moment.utc(durationAsDate).format(format);
}

export function stringItemsToArray(text: any, separator = ',') {
  if (isArray(text)) return text;
  if (typeof text !== 'string' || isNil(text)) return [];
  return (text.split(separator) || []).map((item) => item.trim()).filter(Boolean);
}
