import request from '../request'
import SdkSettings from '../../settings'
import { Context } from '../../api'
import Timeout from '../timeout'

const stagingBaseUrl = 'https://retail-client-events-service-staging.internal.salsify.com'
const stagingEventsEndpoint = `${stagingBaseUrl}/events`
const prodBaseUrl = 'https://retail-client-events-service.internal.salsify.com'
const prodEventsEndpoint = `${prodBaseUrl}/events`

interface Event {
  code: string
  properties: Record<string, unknown>
  timestamp: number
}

const MAX_QUEUE_SIZE = 1000

export default class LogTransport {
  #context: Context
  #settings: SdkSettings
  #eventsEndpoint: string
  #queuedEvents: Array<Event> = []
  #timeout?: Timeout

  public constructor(context: Context, settings: SdkSettings) {
    this.#context = context
    this.#settings = settings
    this.#eventsEndpoint = this.#settings.staging ? stagingEventsEndpoint : prodEventsEndpoint
  }

  public log(code: string, properties: Record<string, unknown>, context: Record<string, unknown>): void {
    const event = {
      code: 'sdk_' + code,
      properties,
      context,
      timestamp: Date.now() / 1000,
    }

    this.#send([event])
  }

  async #send(logs: Array<Event>): Promise<void> {
    const allLogs = [...this.#queuedEvents.splice(0), ...logs]
    if (!allLogs.length) return

    const payload = {
      app: 'sxp_sdk',
      channel: this.#context.clientId,
      csid: this.#context.sessionId,
      pagesessionid: this.#context.pageSessionId,
      jsSource: this.#context.jsSource,
      timestamp: Date.now() / 1000,
      logs: allLogs,
    }

    let response: Response
    try {
      response = await request.post(this.#eventsEndpoint, payload)
    } catch (e) {
      this.#enqueueAndStartTimeout(allLogs)
      return
    }

    if (response.ok) {
      this.#timeout?.clear()
    } else {
      this.#enqueueAndStartTimeout(allLogs)
    }
  }

  #enqueueAndStartTimeout(logs: Array<Event>): void {
    if (this.#queuedEvents.length >= MAX_QUEUE_SIZE) return

    this.#queuedEvents.push(...logs.splice(0, MAX_QUEUE_SIZE - this.#queuedEvents.length))

    if (!this.#timeout) this.#timeout = new Timeout()
    this.#timeout.start(() => this.#send(this.#queuedEvents.splice(0)))
  }
}
