import {
  UserEvent as ApiUserEvent,
  PageVisitCategory,
  SupportedEventResponse,
} from '@spiffy-ai/commerce-api-client';
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import Logger from 'src/application/logging/logger';
import {
  PDPVariantInfo,
  PLPVariantInfo,
  PageVisitVariantInfo,
  UserEvent,
  VariantInfo,
  VariantTypeEnum,
} from 'src/application/models';
import { sessionStorageUtil } from 'src/atoms/atomStore';
import type { PageVariantInfo } from 'src/contexts/pageContext/types';
import { ShowFloatingButtonOptions } from 'src/contexts/types';

const logger = new Logger('variant');

export interface SupportedEvent extends Pick<
  SupportedEventResponse,
  'supported' | 'ready' | 'category' | 'collections' | 'top_category'
> {
  numberOfReviews: number | undefined;
  merchant_tags?: string[];
}

interface PDPAttributes {
  product_id?: string;
}

interface PLPAttributes {
  attributes?: {
    id?: string;
  };
}

export interface UrlResolvingPDPConfig {
  pdp_attributes: PDPAttributes;
  number_of_reviews?: number;
  collections?: string[];
  merchant_tags?: string[];
}

export interface UrlResolvingPLPConfig {
  plp_attributes: PLPAttributes;
  top_category?: string;
}

interface UrlResolvingGenericConfig {
  details?: string;
}

export type PageVariantCheckConfig = {
  check_type: string;
  feature_gate?: string;
  extractor?: {
    type: string;
    map?: Record<string, string[]>;
    value?: string;
  };
  product_id_extractor?: string;
  product_ids?: string[];
  plp_id_extractor?: string;
  plp_ids?: string[];
  collections?: string[];
  query_param?: string;
  category?: string;
  top_category?: string[];
  review_minimum?: number;
  merchant_tags?: string[];
};

export type BackendWidgetMountingConfig = {
  mounting_config_id: string;
  widget_config_id?: string;
  mounting_point_widgets?: Array<{
    checks: PageVariantCheckConfig[];
    widget_config_id: string;
  }>;
};

export type BackendPageVariantConfig = {
  variant_id: string;
  variant_type: string;
  variant_checks: PageVariantCheckConfig[];
  widget_mounting: BackendWidgetMountingConfig[];
  plp_id_extractor?: string;
  product_id_extractor?: string;
  parent_product_id_extractor?: string;
};

export interface UrlResolverResponse {
  variant_type: string;
  specific_details: UrlResolvingPDPConfig | UrlResolvingPLPConfig | UrlResolvingGenericConfig;
  ready: boolean;
  user_event?: ApiUserEvent;
  page_variant?: BackendPageVariantConfig;
}

// Use a map for per-URL cache
export type UrlResolverCache = Record<string, UrlResolverResponse | undefined>;

const internalStorageUrlResolverAtom = atomWithStorage<string | undefined>(
  'spiffy-url-resolver',
  undefined,
  sessionStorageUtil,
  {
    getOnInit: true,
  },
);

export const urlResolverAtom = atom(
  (get): UrlResolverCache => {
    const maybeUrlResolver = get(internalStorageUrlResolverAtom);
    if (maybeUrlResolver == null) {
      return {};
    }
    return JSON.parse(maybeUrlResolver);
  },
  (get, set, value: { url: string; response: UrlResolverResponse | undefined }) => {
    // Get current cache from storage
    const currentCache = get(urlResolverAtom);
    const newCache = { ...currentCache, [value.url]: value.response };

    set(internalStorageUrlResolverAtom, JSON.stringify(newCache));
  },
);

const internalStorageSupportedEventAtom = atomWithStorage<string | undefined>(
  'spiffy-supported-event',
  undefined,
  sessionStorageUtil,
  {
    getOnInit: true,
  },
);
const internalSupportedEventAtom = atom<SupportedEvent | undefined>(undefined);

export const supportedEventAtom = atom(
  (get): SupportedEvent | undefined => {
    const maybeSupportedEvent = get(internalStorageSupportedEventAtom);
    if (maybeSupportedEvent == null) {
      return undefined;
    }

    // TODO this is an unvalidated parse. It may be better to implement a custom storage util
    // and perform the object validation on "get" there.
    return JSON.parse(maybeSupportedEvent);
  },
  (_, set, value: SupportedEvent | undefined) => {
    if (value == null) {
      set(internalStorageSupportedEventAtom, undefined);
      set(internalSupportedEventAtom, undefined);
      return;
    }

    set(internalSupportedEventAtom, value);
    set(internalStorageSupportedEventAtom, JSON.stringify(value));
  },
);

const internalVariantIdAtom = atom<string>();
const internalProductIdAtom = atom<string>();
const internalParentProductIdAtom = atom<string>();
const internalProductUrlAtom = atom<string>();
const internalPlpIdAtom = atom<string>();
const internalUrlAtom = atom<string>();
const internalPageVisitCategoryAtom = atom<PageVisitCategory>();
const internalVariantAtom = atom<'pdp' | 'plp' | 'search' | 'page_visit' | 'full_page'>('pdp');
const internalFloatingButtonOverrideAtom = atom<ShowFloatingButtonOptions>();

export const variantIdAtom = atom(get => get(internalVariantIdAtom));
export const productIdAtom = atom(get => get(internalProductIdAtom));
export const parentProductIdAtom = atom(get => get(internalParentProductIdAtom));
export const productUrlAtom = atom(get => get(internalProductUrlAtom));
export const plpIdAtom = atom(get => get(internalPlpIdAtom));
export const urlAtom = atom(get => get(internalUrlAtom));
export const pageVisitCategoryAtom = atom(get => get(internalPageVisitCategoryAtom));
export const variantAtom = atom(get => get(internalVariantAtom));
export const floatingButtonOverrideAtom = atom(get => get(internalFloatingButtonOverrideAtom));
export const hasParsedVariantInfoAtom = atom(false);

export const variantInfoAtom = atom(
  (get): VariantInfo => {
    const variant = get(variantAtom);
    // TODO: Maybe I need to return the fbo here too.
    if (variant === VariantTypeEnum.Pdp) {
      return {
        variantId: get(variantIdAtom),
        variant,
        productId: get(productIdAtom),
        parentProductId: get(parentProductIdAtom),
        url: get(urlAtom),
        floatingButtonOverride: get(floatingButtonOverrideAtom),
      } as PDPVariantInfo;
    }
    if (variant === VariantTypeEnum.Plp) {
      return {
        variantId: get(variantIdAtom),
        variant,
        plpId: get(plpIdAtom),
        url: get(urlAtom),
        floatingButtonOverride: get(floatingButtonOverrideAtom),
      } as PLPVariantInfo;
    }
    if (variant === VariantTypeEnum.PageVisit) {
      return {
        variantId: get(variantIdAtom),
        variant,
        url: get(urlAtom),
        pageVisitCategory: get(pageVisitCategoryAtom),
        floatingButtonOverride: get(floatingButtonOverrideAtom),
      } as PageVisitVariantInfo;
    }
    throw new Error('Invalid variantInfo details');
  },
  (_, set, newVariant: VariantInfo) => {
    set(internalVariantAtom, newVariant.variant);
    set(internalVariantIdAtom, newVariant.variantId);
    set(internalFloatingButtonOverrideAtom, newVariant.floatingButtonOverride);
    if (newVariant.variant === VariantTypeEnum.Pdp) {
      set(internalProductIdAtom, newVariant.productId);
      set(internalParentProductIdAtom, newVariant.parentProductId);
      set(internalUrlAtom, newVariant.url);
    }
    if (newVariant.variant === VariantTypeEnum.Plp) {
      set(internalPlpIdAtom, newVariant.plpId);
      set(internalUrlAtom, newVariant.url);
    }
    if (newVariant.variant === VariantTypeEnum.PageVisit) {
      set(internalUrlAtom, newVariant.url);
      set(internalPageVisitCategoryAtom, newVariant.pageVisitCategory);
    }
  },
);

const getPageVisitContext = (pageVisitCategory: PageVisitCategory | undefined) => {
  if (pageVisitCategory === PageVisitCategory.Search) {
    const searchParams = new URLSearchParams(window.location.search);
    const searchTerm = searchParams.get('esq');
    return {
      page_type: 'search',
      page_id: searchTerm,
    };
  }
  if (pageVisitCategory === PageVisitCategory.Homepage) {
    return {
      page_type: 'homepage',
      page_id: window.location.href,
    };
  }
  if (pageVisitCategory === PageVisitCategory.FullPage) {
    return {
      page_type: 'full_page',
      page_id: window.location.href,
    };
  }
  return {
    page_type: 'other',
    page_id: window.location.href,
  };
};

export const analyticsContextAtom = atom(get => {
  const variantInfo = get(variantInfoAtom);
  if (variantInfo.variant === VariantTypeEnum.Pdp) {
    return {
      page_type: 'pdp',
      page_id: variantInfo.productId,
    };
  }
  if (variantInfo.variant === VariantTypeEnum.Plp) {
    return {
      page_type: 'plp',
      page_id: variantInfo.plpId,
    };
  }
  if (variantInfo.variant === VariantTypeEnum.PageVisit) {
    return getPageVisitContext(variantInfo.pageVisitCategory);
  }
  logger.logWarn('Invalid variantInfo', undefined, { variantInfo });
  return {
    page_type: 'other',
    page_id: window.location.href,
  };
});

// Atoms for page context - can be set directly by PageProvider or for storybook/testing
export const pageUserEventAtom = atom<UserEvent | undefined>(undefined);
export const pageVariantInfoAtom = atom<PageVariantInfo | undefined>(undefined);
