import { PLPAttributeCategory, UserEventCategory } from '@spiffy-ai/commerce-api-client';
import { SearchResult } from 'src/application/models/api/search';
import { UserEvent, VariantInfo } from 'src/application/models';
import { v4 as uuid } from 'uuid';

export const isElementPartiallyVisible = (el?: HTMLDivElement | null) => {
  if (!el) return false;

  const rect = el.getBoundingClientRect();
  const windowHeight = window.innerHeight || document.documentElement.clientHeight;
  const windowWidth = window.innerWidth || document.documentElement.clientWidth;
  const verticallyVisible = Math.round(rect.top) < windowHeight && Math.round(rect.bottom) > 0;
  const horizontallyVisible = Math.round(rect.left) < windowWidth && Math.round(rect.right) > 0;
  return verticallyVisible && horizontallyVisible;
};

export const createAppLoadedEvent = (): UserEvent => ({
  eventId: uuid(),
  createdAt: new Date().toISOString(),
  category: UserEventCategory.AppLoaded,
});

export const createVisitUserEvent = ({
  variantInfo,
}: {
  variantInfo: VariantInfo;
}): UserEvent | undefined => {
  // this is a pdp visit event
  if (variantInfo.variant === 'pdp' && variantInfo.productId != null) {
    return {
      eventId: uuid(),
      createdAt: new Date().toISOString(),
      category: UserEventCategory.PdpVisit,
      attributes: {
        productId: variantInfo.productId,
        parentProductId: variantInfo.parentProductId ?? '',
        url: variantInfo.url ?? '',
      },
    };
  }

  // this is a plp visit event
  if (variantInfo.variant === 'plp' && variantInfo.plpId != null) {
    return {
      eventId: uuid(),
      createdAt: new Date().toISOString(),
      category: UserEventCategory.PlpVisit,
      attributes: {
        category: PLPAttributeCategory.Id,
        attributes: {
          id: variantInfo.plpId,
        },
      },
    };
  }

  if (variantInfo.variant === 'page_visit') {
    return {
      eventId: uuid(),
      createdAt: new Date().toISOString(),
      category: UserEventCategory.PageVisit,
      attributes: {
        url: variantInfo.url,
        pageVisitCategory: variantInfo.pageVisitCategory,
      },
    };
  }
  return undefined;
};

const parseTime = (time: string, timeZone: string) => {
  const times = time.match(/^([0-1]?\d):([0-5]\d)(AM|PM)$/i);
  const hours = times?.[1];
  const minutes = times?.[2];
  const period = times?.[3];

  if (hours && minutes && period) {
    const date = new Date();

    // Adjust hours for AM/PM
    let adjustedHours = 0;
    if (period.toUpperCase() === 'PM' && hours !== '12') {
      adjustedHours = parseInt(hours, 10) + 12;
    }

    if (period.toUpperCase() === 'AM' && hours !== '12') {
      adjustedHours = parseInt(hours, 10);
    }

    // Create the date string with time zone
    const formattedDate = `${date.toISOString().split('T')[0]}T${String(adjustedHours).padStart(
      2,
      '0',
    )}:${minutes}:00`;
    return new Date(`${formattedDate}${timeZone}`);
  }
  return undefined;
};

export const isWithinBusinessHours = (startTime: string, endTime: string, timeZone: string) => {
  // Parse start and end times
  const start = parseTime(startTime, timeZone);
  let end = parseTime(endTime, timeZone);

  if (!start || !end) {
    return false;
  }

  let now = new Date();

  // If the end date is greater than the start date, add a day to both of them
  // This handles configurations such as startTime = 10:00PM and endTime = 7:00AM
  if (end < start) {
    end = new Date(end.getTime() + 24 * 60 * 60 * 1000);
    now = new Date(now.getTime() + 24 * 60 * 60 * 1000);
  }

  // If the date conversion shifts the end date to the next day, consider comparing it across midnight
  if (end.getUTCDate() > start.getUTCDate()) {
    const crossingMidnight = new Date(now.getTime() + 24 * 60 * 60 * 1000);
    return start <= now || crossingMidnight <= end;
  }

  // Check if the current time falls between start and end
  return now >= start && now <= end;
};

export enum SearchResultsState {
  Loading,
  Results,
  NoResults,
}

export const getSearchResultsState = (
  isLoadingSearch: boolean,
  searchData: SearchResult | null,
): SearchResultsState => {
  if (isLoadingSearch) {
    return SearchResultsState.Loading;
  }
  if (searchData) {
    return SearchResultsState.Results;
  }
  return SearchResultsState.NoResults;
};
