import { display } from '../tools/display'
import { ONE_MINUTE, ONE_SECOND } from '../tools/utils/timeUtils'
import {
  findAllCommaSeparatedValues,
  findCommaSeparatedValue,
  findCommaSeparatedValues,
  generateUUID,
} from '../tools/utils/stringUtils'
import { buildUrl } from '../tools/utils/urlPolyfill'

export interface CookieOptions {
  secure?: boolean
  crossSite?: boolean
  partitioned?: boolean
  domain?: string
}

export function setCookie(name: string, value: string, expireDelay: number = 0, options?: CookieOptions) {
  const date = new Date()
  date.setTime(date.getTime() + expireDelay)
  const expires = `expires=${date.toUTCString()}`
  const sameSite = options && options.crossSite ? 'none' : 'strict'
  const domain = options && options.domain ? `;domain=${options.domain}` : ''
  const secure = options && options.secure ? ';secure' : ''
  const partitioned = options && options.partitioned ? ';partitioned' : ''
  document.cookie = `${name}=${value};${expires};path=/;samesite=${sameSite}${domain}${secure}${partitioned}`
}

/**
 * Returns the value of the cookie with the given name
 * If there are multiple cookies with the same name, returns the first one
 */
export function getCookie(name: string) {
  return findCommaSeparatedValue(document.cookie, name)
}

/**
 * Returns all the values of the cookies with the given name
 */
export function getCookies(name: string): string[] {
  return findAllCommaSeparatedValues(document.cookie).get(name) || []
}

let initCookieParsed: Map<string, string> | undefined

/**
 * Returns a cached value of the cookie. Use this during SDK initialization (and whenever possible)
 * to avoid accessing document.cookie multiple times.
 *
 * ⚠️ If there are multiple cookies with the same name, returns the LAST one (unlike `getCookie()`)
 */
export function getInitCookie(name: string) {
  if (!initCookieParsed) {
    initCookieParsed = findCommaSeparatedValues(document.cookie)
  }
  return initCookieParsed.get(name)
}

export function resetInitCookies() {
  initCookieParsed = undefined
}

export function deleteCookie(name: string, options?: CookieOptions) {
  setCookie(name, '', 0, options)
}

export function areCookiesAuthorized(options: CookieOptions): boolean {
  if (document.cookie === undefined || document.cookie === null) {
    return false
  }
  try {
    // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during
    // the test cookie lifetime
    const testCookieName = `dd_cookie_test_${generateUUID()}`
    const testCookieValue = 'test'
    setCookie(testCookieName, testCookieValue, ONE_MINUTE, options)
    const isCookieCorrectlySet = getCookie(testCookieName) === testCookieValue
    deleteCookie(testCookieName, options)
    return isCookieCorrectlySet
  } catch (error) {
    display.error(error)
    return false
  }
}

/**
 * No API to retrieve it, number of levels for subdomain and suffix are unknown
 * strategy: find the minimal domain on which cookies are allowed to be set
 * https://web.dev/same-site-same-origin/#site
 */
let getCurrentSiteCache: string | undefined
export function getCurrentSite(hostname = location.hostname, referrer = document.referrer): string | undefined {
  if (getCurrentSiteCache === undefined) {
    const defaultHostName = getCookieDefaultHostName(hostname, referrer)
    if (defaultHostName) {
      // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during
      // the test cookie lifetime
      const testCookieName = `dd_site_test_${generateUUID()}`
      const testCookieValue = 'test'

      const domainLevels = defaultHostName.split('.')
      let candidateDomain = domainLevels.pop()!
      while (domainLevels.length && !getCookie(testCookieName)) {
        candidateDomain = `${domainLevels.pop()!}.${candidateDomain}`
        setCookie(testCookieName, testCookieValue, ONE_SECOND, { domain: candidateDomain })
      }
      deleteCookie(testCookieName, { domain: candidateDomain })
      getCurrentSiteCache = candidateDomain
    }
  }

  return getCurrentSiteCache
}

function getCookieDefaultHostName(hostname: string, referrer: string) {
  try {
    return hostname || buildUrl(referrer).hostname
  } catch {
    // Ignore
  }
}

export function resetGetCurrentSite() {
  getCurrentSiteCache = undefined
}
