import { parseISO } from 'date-fns';
import { utcToZonedTime } from 'date-fns-tz';
import { cloneDeep, isEqual } from 'lodash-es';

import { queryClient } from 'src/config/react-query';
import { urlToFile } from 'src/utils/stringUtils';
import { getBrowserTimeZone } from 'src/utils/timeUtils';
import { getTValidations } from 'src/utils/validation';

import { type LearningAudience } from '../../common/types';
import {
  formatLearnignSegmentation,
  formatLearningAudience,
} from '../../common/utils';
import { SessionStatus, SessionSteps } from '../constants';
import { sessionsKeys } from '../queries';
import {
  type NewSessionFormValues,
  type SessionBasicInformation,
  type SessionDetail,
  type SessionStepsType,
} from '../types';

import {
  formatSessionBasicInformation,
  formatSessionConfiguration,
} from './formatters';

const { BASIC_INFORMATION, CONFIGURATION, SEGMENTATION, REVISION } =
  SessionSteps;

const SESSION_NEW_DEFAULT_VALUES: NewSessionFormValues = {
  [SessionSteps.BASIC_INFORMATION]: {
    id: undefined,
    timeZone: getBrowserTimeZone(),
    title: '',
    description: '',
    status: SessionStatus.draft,
    isAudienceDefined: false,
    coverPicture: undefined,
    localDate: null,
    startTime: null,
    endTime: null,
  },
  [SessionSteps.CONFIGURATION]: {
    type: 'PRESENTIAL',
    address: '',
    meetingLink: '',
    collaborator: '',
    sendNotification: false,
    isObligatory: true,
    issueCertificate: false,
  },
  [SessionSteps.SEGMENTATION]: null,
};

export const formatDefaultSessionValues = async (
  sessionData?: SessionDetail,
): Promise<NewSessionFormValues> => {
  if (!sessionData) return SESSION_NEW_DEFAULT_VALUES;

  const pictureFile = sessionData.pictureUrl
    ? await urlToFile(sessionData.pictureUrl)
    : null;

  const sessionAudience: LearningAudience = {
    isPublic: sessionData.isPublic,
    isAudienceDefined: sessionData.isAudienceDefined,
    segmentationItemIds:
      sessionData.audiencesQuery?.conditions[0].segmentationItemIds || [],
    segmentationUsers: sessionData.audiencesQuery?.conditions[0].users || [],
  };

  return {
    basic_information: {
      title: sessionData.title,
      description: sessionData.description,
      id: sessionData.id,
      status: sessionData.status,
      isAudienceDefined: sessionData.isAudienceDefined,
      localDate: sessionData.localDate
        ? parseISO(sessionData.localDate)
        : undefined,
      startTime: utcToZonedTime(sessionData.startDate, sessionData.timeZone),
      endTime: utcToZonedTime(sessionData.endDate, sessionData.timeZone),
      timeZone: sessionData.timeZone,
      coverPicture: {
        original: pictureFile,
        cropped: pictureFile,
        url: sessionData.pictureUrl,
      },
    },
    configuration: {
      type: sessionData.type,
      address: sessionData.address || '',
      meetingLink: sessionData.meetingLink || '',
      collaborator: sessionData.collaborator || '',
      sendNotification: sessionData.sendNotification ?? false,
      isObligatory: sessionData.isObligatory ?? true,
      issueCertificate: sessionData.issueCertificate ?? false,
    },
    segmentation: formatLearningAudience(sessionAudience),
  } as NewSessionFormValues;
};

export const updateSession = (
  newSession: SessionDetail,
  currentValues: NewSessionFormValues,
): NewSessionFormValues => {
  const newBasicInformation: SessionBasicInformation = {
    ...currentValues.basic_information,
    id: newSession.id,
    coverPicture: {
      original: currentValues.basic_information.coverPicture?.original,
      cropped: currentValues.basic_information.coverPicture?.cropped,
      url: newSession.pictureUrl,
    },
  };

  return {
    ...currentValues,
    basic_information: newBasicInformation,
  };
};

export const invalidateSessionsQueries = (id?: number | null) => {
  queryClient.invalidateQueries(sessionsKeys.all());
  if (id) {
    queryClient.invalidateQueries(sessionsKeys.sessions.detail(id));
  }
};

export const getUpdatedSession = (
  currentValues: NewSessionFormValues,
  newSession?: SessionDetail,
) => {
  let updatedValues = currentValues;
  if (newSession) {
    updatedValues = updateSession(newSession, currentValues);
  }
  return cloneDeep(updatedValues);
};

export const hasSessionStepChanges = (
  prevValues: NewSessionFormValues | undefined,
  currentValues: NewSessionFormValues | undefined,
  step: keyof NewSessionFormValues,
  isAudienceDefined?: boolean,
) => {
  if (!prevValues || !currentValues) return false;

  // Edge case: if audience is not defined, we need to force to set the audience call
  if (!isAudienceDefined && step === 'segmentation') return true;

  return !isEqual(prevValues[step], currentValues[step]);
};

export const getSessionStepFormatter = (step: SessionStepsType) => {
  const formatters = {
    basic_information: formatSessionBasicInformation,
    configuration: formatSessionConfiguration,
    segmentation: formatLearnignSegmentation,
  };

  return formatters[step as keyof typeof formatters];
};

export const newSessionFields = {
  [BASIC_INFORMATION]: {
    all: () => BASIC_INFORMATION,
    title: () => `${BASIC_INFORMATION}.title` as const,
    description: () => `${BASIC_INFORMATION}.description` as const,
    localDate: () => `${BASIC_INFORMATION}.localDate` as const,
    startTime: () => `${BASIC_INFORMATION}.startTime` as const,
    endTime: () => `${BASIC_INFORMATION}.endTime` as const,
    timeZone: () => `${BASIC_INFORMATION}.timeZone` as const,
    coverPicture: () => `${BASIC_INFORMATION}.coverPicture` as const,
  },
  [CONFIGURATION]: {
    all: () => CONFIGURATION,
    type: () => `${CONFIGURATION}.type` as const,
    address: () => `${CONFIGURATION}.address` as const,
    meetingLink: () => `${CONFIGURATION}.meetingLink` as const,
    collaborator: () => `${CONFIGURATION}.collaborator` as const,
    isObligatory: () => `${CONFIGURATION}.isObligatory` as const,
    sendNotification: () => `${CONFIGURATION}.sendNotification` as const,
    issueCertificate: () => `${CONFIGURATION}.issueCertificate` as const,
  },
  [SEGMENTATION]: {
    all: () => SEGMENTATION,
    userSegmentation: () => `${SEGMENTATION}.userSegmentation` as const,
    groupSegmentation: () => `${SEGMENTATION}.groupSegmentation` as const,
  },
  [REVISION]: {},
};

type ValidateDateReferences = {
  startTime: Date | null;
  isOld: boolean;
  now: Date;
};

export const newSessionRules = {
  [BASIC_INFORMATION]: {
    title: () => ({ requiredString: true, maxString: 140, minString: 3 }),
    description: () => ({ maxString: 1000 }),
    localDate: () => ({ requiredDate: true }),
    startTime: () => ({ requiredTime: true }),
    endTime: ({ startTime, isOld, now }: ValidateDateReferences) => ({
      requiredTime: true,
      afterDate: {
        date: startTime ?? new Date(''),
        message: getTValidations('hour.end_hour_must_be_after_start_hour'),
      },
      ...(isOld && {
        beforeDate: {
          date: now,
          message: getTValidations('hour.end_hour_must_be_before_now'),
        },
      }),
    }),
  },
  [CONFIGURATION]: {
    address: () => ({ requiredString: true, maxString: 140, minString: 3 }),
    meetingLink: ({ required = false } = {}) => ({
      requiredString: required,
      maxString: 250,
    }),
    collaborator: () => ({ maxString: 100 }),
  },
};
