/**
 * @see https://docs.developers.optimizely.com/web/docs
 *
 * globalThis.optimizely?.get('state').getVariationMap()
 * {
 *   20015858428: { id: "20020533131", name: null, index: null },
 *   20104572923: { id: "20121934640", name: "v1", index: 0 },
 * }
 */

/**
 * Dictionary: Record replacement, handle undefined values.
 * @see https://fnune.com/typescript/2019/01/30/typescript-series-1-record-is-usually-not-the-best-choice/
 */
type DictionaryKey = string | number | symbol
export type Dictionary<K extends DictionaryKey, T> = { [P in K]?: T }

type Option = { id: string; name: string }

// ------

type CampaignState = {
  allExperiments: Option[]
  audiences: Option[]
  campaignName: string | null
  experiment: Option | null
  id: string
  isActive: boolean
  isInCampaignHoldback: boolean
  reason: undefined | string // 'force'
  variation: Option
  visitorRedirected: boolean
}

type Command = { type: string; eventName: string; tags: Object }

type ConditionOperator = 'or' | 'and'
type ConditionValue = { type: string; value: string; match: string }
type Condition = ConditionOperator | ConditionValue | Condition[]

type Decision = { campaign?: string; experiment: string; variation: string; holdback?: boolean }
type DecisionConfig = { campaignId: string; shouldCleanString: boolean; maxLength: number }

type EventBase = { c: string; hash_: string; n: string; y: string }
type Event = { eventBase: EventBase; s: number; si: number; t: number }

type ExperimentState = {
  audiences: Option[]
  experimentName: string
  id: string
  isActive: boolean
  isInExperimentHoldback: boolean
  reason?: string | unknown
  variation: Option
  visitorRedirected: boolean
}

type FilterObject = { isActive?: boolean }
type FilterFunction = (campaignState: CampaignState) => boolean
type Filter = FilterObject | FilterFunction

type GetKey =
  | 'behavior'
  | 'data'
  | 'dcp'
  | 'jquery'
  | 'session'
  | 'state'
  | 'utils'
  | 'visitor'
  | 'visitor_id'

type Location = { city: string; continent: string; country: string; region: string }

type PageTag = {
  apiName: string
  category: string
  locator: string
  locatorType: string
  valueType: string
}

type PageState = {
  apiName: string
  category: string
  id: string
  isActive: boolean
  metadata: Object
  name: string
  staticConditions: Condition[]
  tags: PageTag[]
}

type RedirectInfo = { experimentId: string; variationId: string; referrer: string }

type Variation = { id: string; name: string | null; index: number | null }
type VariationMap = Dictionary<string, Variation>

type Visitor = {
  browserId: string // 'gc'
  browserVersion: string
  currentTimestamp: number
  customBehavior: unknown
  device: string // 'desktop'
  device_type: string // 'desktop_laptop'
  events: Event[]
  first_session: boolean
  location: Location
  offset: number
  referrer: null | unknown
  source_type: string
  visitorId: string
}

type VisitorId = { randomId: string }

// ------

export interface OptimizelyState {
  getActivationId: () => string
  getActiveExperimentIds: () => string[]
  getCampaignStateLists: () => Dictionary<string, CampaignState[]>
  getCampaignStates: (filter?: Filter) => Dictionary<string, CampaignState>
  getDecisionObject: (config: DecisionConfig) => Decision | null
  getDecisionString: (config: DecisionConfig) => string | null
  getExperimentStates: (filter?: Filter) => Dictionary<string, ExperimentState>
  getPageStates: (filter?: Filter) => Dictionary<string, PageState>
  getRedirectInfo: () => RedirectInfo | null
  getVariationMap: () => VariationMap
  isGlobalHoldback: () => boolean
}

export interface Optimizely {
  get: (key: GetKey) => OptimizelyState | Visitor | VisitorId | unknown // others omitted
  push: (command: Command) => void
}