import { formatISO, intervalToDuration, isAfter, startOfDay } from 'date-fns';
import { mapValues, pickBy, sortBy } from 'lodash-es';

import { PROFILE_DATA_CONSTANTS } from 'src/constants/instance';
import { useRequiredAuth } from 'src/contexts/JWTContext';
import { type MeInstance, type ProfileDataMap } from 'src/types/instance';
import { type UserProfile } from 'src/types/user';
import { insertIf } from 'src/utils/arrays';
import { simpleDateStringToLocalDate } from 'src/utils/date';
import { type LokaliseTFunction, useLokaliseTranslation } from 'src/utils/i18n';
import {
  CUSTOM_FIELD_DEFAULT_VALUE_MAP,
  getFullName,
} from 'src/utils/userUtils';

import { MONTHLY_EXPENSE_LIMIT } from 'src/components/dashboard/form/formDetail/panalab/constants';

import { SOCIAL_KEYS } from './constants';
import { ProfileInputType } from './types';

export const getInputs = (
  instance: MeInstance,
  t: LokaliseTFunction,
  profileData: ProfileDataMap = {},
) => {
  const basicInputs = [
    ...insertIf(instance.editableName, {
      name: 'firstName',
      label: t('first_name'),
      required: true,
      type: ProfileInputType.STRING,
    }),
    ...insertIf(instance.editableName, {
      name: 'lastName',
      label: t('last_name'),
      required: true,
      type: ProfileInputType.STRING,
    }),
    ...insertIf(instance.allowNickname, {
      name: 'nickname',
      label: t('nick_name'),
      type: ProfileInputType.STRING,
    }),
    ...insertIf(instance.editableBirthdate, {
      name: 'birthdate',
      label: t('birthdate'),
      type: ProfileInputType.DATE,
      disableFuture: true,
    }),
    ...insertIf(instance.editableEmail, {
      name: 'email',
      label: t('email'),
      type: ProfileInputType.EMAIL,
    }),
    {
      name: 'phoneNumber',
      label: t('phone_number'),
      type: ProfileInputType.PHONE_NUMBER,
    },
  ];

  const socialInputs = [
    ...insertIf(instance.allowSocialNetworks?.allowLinkedin, {
      name: 'linkedIn',
      label: t('linked_in'),
      type: ProfileInputType.URL,
      helperText: t('linkedin_placeholder'),
    }),
    ...insertIf(instance.allowSocialNetworks?.allowInstagram, {
      name: 'instagram',
      label: t('instagram'),
      type: ProfileInputType.URL,
      helperText: t('instagram_placeholder'),
    }),
    ...insertIf(instance.allowSocialNetworks?.allowFacebook, {
      name: 'facebook',
      label: t('facebook'),
      type: ProfileInputType.URL,
      helperText: t('facebook_placeholder'),
    }),
    ...insertIf(instance.allowSocialNetworks?.allowTwitter, {
      name: 'twitter',
      label: t('X'),
      type: ProfileInputType.URL,
      helperText: t('x_placeholder'),
    }),
  ];

  const orderedProfileData = sortBy(
    Object.entries(profileData),
    ([, field]) => field.position,
  );
  const profileDataInputs = orderedProfileData.map(pf => ({
    name: `profileData.${pf[0]}`,
    label: pf[1].name,
    type: pf[1].type as ProfileInputType,
    id: pf[0],
    options: pf[1].options || [],
    suggestions: pf[1].suggestions || [],
    metadata: { showCounter: true },
  }));

  return [...basicInputs, ...socialInputs, ...profileDataInputs].map(input => ({
    ...input,
    label:
      'required' in input && input.required ? `${input.label} *` : input.label,
  }));
};

export const filterProfileData = (profileData: ProfileDataMap) =>
  pickBy(
    profileData,
    value =>
      value.editableByUser &&
      value.name !== MONTHLY_EXPENSE_LIMIT &&
      value.type in PROFILE_DATA_CONSTANTS,
  );

/**
 * Backend only sends profile fields that are filled out by the user.
 * This function formats received fields for the form and adds default values
 * to any missing fields.
 */
export const formatProfileDataForForm = (
  profileFields: ProfileDataMap,
  user: UserProfile,
) =>
  mapValues(profileFields, (field, fieldId) => {
    if (field.type === PROFILE_DATA_CONSTANTS.STRING) {
      const rawValue = user.profileData[fieldId]?.value;
      if (field.suggestions?.length) {
        return rawValue
          ? { label: String(rawValue), value: String(rawValue) }
          : null;
      } else {
        return rawValue?.toString() || '';
      }
    }

    if (!(fieldId in user.profileData)) {
      return CUSTOM_FIELD_DEFAULT_VALUE_MAP[field.type];
    }

    switch (field.type) {
      case PROFILE_DATA_CONSTANTS.USER: {
        const u = user.profileData[fieldId].value as UserProfile;
        return [
          {
            label: getFullName(u),
            value: u.id,
            id: u.id,
          },
        ];
      }
      case PROFILE_DATA_CONSTANTS.USER_LIST:
        return (user.profileData[fieldId].value as UserProfile[]).map(u => ({
          label: getFullName(u),
          value: u.id,
          id: u.id,
        }));
      case PROFILE_DATA_CONSTANTS.OPTION:
        return {
          label: String(user.profileData[fieldId].value),
          value: user.profileData[fieldId].value,
        };
      case PROFILE_DATA_CONSTANTS.MULTIPLE_OPTION:
        return (user.profileData[fieldId].value as string[]).map(v => ({
          label: String(v),
          value: v,
        }));
      case PROFILE_DATA_CONSTANTS.DATE:
        return new Date(user.profileData[fieldId].value as string);
      case PROFILE_DATA_CONSTANTS.STRING_LIST:
        return (user.profileData[fieldId].value as string[]).map(v => ({
          label: v,
          value: v,
        }));
      case PROFILE_DATA_CONSTANTS.NUMBER_LIST:
        return (user.profileData[fieldId].value as number[]).map(v => ({
          label: String(v),
          value: v,
        }));
      default:
        return user.profileData[fieldId].value;
    }
  });

const formatValueBeforeSubmit = (
  value: any,
  type: keyof typeof PROFILE_DATA_CONSTANTS,
) => {
  switch (type) {
    case PROFILE_DATA_CONSTANTS.NUMBER:
      return String(value).length > 0 ? parseFloat(value) : null;
    case PROFILE_DATA_CONSTANTS.NUMBER_LIST:
      return value.length > 0
        ? value.map((v: { value: string }) => parseFloat(v.value))
        : null;
    case PROFILE_DATA_CONSTANTS.USER:
      return value.length > 0 ? value[0].id : null;
    case PROFILE_DATA_CONSTANTS.USER_LIST:
      return value.length > 0
        ? value.map((user: UserProfile) => user.id)
        : null;
    case PROFILE_DATA_CONSTANTS.DATE:
      return value ? formatISO(value, { representation: 'date' }) : null;
    case PROFILE_DATA_CONSTANTS.OPTION:
      return value ? String(value.value) : null;
    case PROFILE_DATA_CONSTANTS.MULTIPLE_OPTION:
    case PROFILE_DATA_CONSTANTS.STRING_LIST:
      return value.length > 0
        ? value.map((v: { value: unknown }) => String(v.value))
        : null;
    case PROFILE_DATA_CONSTANTS.STRING:
      if (value && typeof value === 'object' && 'value' in value) {
        return String(value.value) || null;
      }
      return value || null;
    default:
      return value;
  }
};

export const formatProfileDataBeforeSubmit = (
  dirtyValues: Record<string, any>,
  profileFields: ProfileDataMap,
) => {
  if (dirtyValues.birthdate) {
    dirtyValues.birthdate = formatISO(dirtyValues.birthdate, {
      representation: 'date',
    });
  }
  if ('phoneNumber' in dirtyValues) {
    dirtyValues.phoneNumber = dirtyValues.phoneNumber || null;
  }

  SOCIAL_KEYS.forEach(key => {
    if (key in dirtyValues && !dirtyValues[key]) {
      dirtyValues[key] = null;
    }
  });

  if (dirtyValues.profileData) {
    dirtyValues.profileData = mapValues(dirtyValues.profileData, (value, key) =>
      formatValueBeforeSubmit(
        value,
        profileFields[key].type as keyof typeof PROFILE_DATA_CONSTANTS,
      ),
    );
  }
  return dirtyValues;
};

export const useWhatsappLink = (user: UserProfile) => {
  const WHATSAPP_API = 'https://api.whatsapp.com/send?phone=+';
  const QUERY_TEXT = '&text=';
  const { t } = useLokaliseTranslation('profile');
  const { user: loggedUser, instance } = useRequiredAuth();
  return (
    WHATSAPP_API +
    user.phoneNumber +
    QUERY_TEXT +
    t('WHATSAPP_MESSAGE', {
      receiver: user.firstName,
      sender: getFullName(loggedUser),
      company: instance.name,
    })
  );
};

export const getSeniorityDuration = (startDate?: string, endDate?: string) => {
  const todayAtMidnight = startOfDay(new Date());
  const end = endDate
    ? startOfDay(simpleDateStringToLocalDate(endDate))
    : todayAtMidnight;
  const start = startDate
    ? startOfDay(simpleDateStringToLocalDate(startDate))
    : todayAtMidnight;

  if (isAfter(start, end)) {
    return { years: 0, months: 0, days: 0 };
  }

  return intervalToDuration({ start, end });
};
