'use client'

import { forwardRef, HTMLAttributes, MouseEvent as ReactMouseEvent, Ref, useEffect, useRef } from 'react'

import { PktButton } from '../button/Button'
import { consentStrings } from './strings'

interface ConsentItem {
  name: string
  consent: boolean
}

interface ConsentValue {
  items: ConsentItem[]
}

interface CookieConsentEvent {
  value: string | ConsentValue
}

type ConsentDetailsMap = Record<string, boolean>

type CookieEventHandler = (consent: CookieConsentEvent) => void

interface CookieEvents {
  on(eventName: string, handler: CookieEventHandler): void
  off(eventName: string, handler: CookieEventHandler): void
}

interface CookieBannerApi {
  cookieConsent: {
    validateConsentCookie(): Promise<boolean>
    getConsentCookie(): string | ConsentValue
  }
  openCookieModal(): void
}

declare global {
  interface Window {
    cookieBanner_devMode?: boolean
    cookieBanner_cookieDomain?: string | null
    cookieBanner_cookieSecure?: string | null
    cookieBanner_cookieExpiryDays?: string | null
    cookieBanner_googleAnalyticsId?: string | null
    cookieBanner_hotjarId?: string | null
    cookieBanner?: CookieBannerApi
    __cookieEvents?: CookieEvents
  }
}

let consentScriptPromise: Promise<void> | null = null

const loadConsentScript = (): Promise<void> => {
  if (consentScriptPromise) return consentScriptPromise

  consentScriptPromise = new Promise((resolve, reject) => {
    if (document.querySelector('#oslo-consent-script')) {
      resolve()
      return
    }
    const script = document.createElement('script')
    script.src = 'https://cdn.web.oslo.kommune.no/cb/cb-v1.1.0.js'
    script.id = 'oslo-consent-script'
    script.onload = () => resolve()
    script.onerror = reject
    document.head.appendChild(script)

    const styles = document.createElement('link')
    styles.href = 'https://cdn.web.oslo.kommune.no/cb/cb-v1.1.0.css'
    styles.type = 'text/css'
    styles.rel = 'stylesheet'
    styles.id = 'oslo-consent-styles'
    document.head.appendChild(styles)
  })

  return consentScriptPromise
}

const parseJsonOrPassThrough = <T,>(value: T | string): T => {
  if (typeof value !== 'string') return value
  try {
    return JSON.parse(value) as T
  } catch {
    return value as unknown as T
  }
}

const toConsentDetails = (consent: CookieConsentEvent): ConsentDetailsMap => {
  const parsed = parseJsonOrPassThrough<ConsentValue>(consent.value)
  return parsed.items.reduce<ConsentDetailsMap>((acc, item) => {
    acc[item.name] = item.consent
    return acc
  }, {})
}

export type TPktConsentTriggerType = 'button' | 'link' | 'footerlink' | 'icon'

export interface IPktConsent extends HTMLAttributes<HTMLElement> {
  devMode?: boolean
  cookieDomain?: string | null
  cookieSecure?: string | null
  cookieExpiryDays?: string | null
  hotjarId?: string | null
  googleAnalyticsId?: string | null
  i18nLanguage?: string
  triggerType?: TPktConsentTriggerType | null
  triggerText?: string | null
  onToggleConsent?: (e: CustomEvent<ConsentDetailsMap>) => void
}

export const PktConsent = forwardRef<HTMLElement, IPktConsent>(function PktConsent(
  {
    devMode = false,
    cookieDomain = null,
    cookieSecure = null,
    cookieExpiryDays = null,
    hotjarId = null,
    googleAnalyticsId = null,
    i18nLanguage = 'nb',
    triggerType = 'button',
    triggerText,
    onToggleConsent,
    ...rest
  },
  ref,
) {
  const onToggleRef = useRef(onToggleConsent)
  onToggleRef.current = onToggleConsent

  const configRef = useRef({
    devMode,
    cookieDomain,
    cookieSecure,
    cookieExpiryDays,
    hotjarId,
    googleAnalyticsId,
  })
  configRef.current = {
    devMode,
    cookieDomain,
    cookieSecure,
    cookieExpiryDays,
    hotjarId,
    googleAnalyticsId,
  }

  const triggerInitRef = useRef<() => void>(() => {})

  const language = (i18nLanguage in consentStrings.i18n ? i18nLanguage : 'nb') as keyof typeof consentStrings.i18n
  const resolvedTriggerText = triggerText ?? consentStrings.i18n[language].contentPresentation.buttons.settings

  useEffect(() => {
    let cancelled = false
    let cookieEventHandler: CookieEventHandler | undefined

    const config = configRef.current
    // cookieBanner_* identifiers are defined by the external cookie banner script
    /* eslint-disable camelcase */
    window.cookieBanner_googleAnalyticsId = config.googleAnalyticsId
    window.cookieBanner_hotjarId = config.hotjarId
    if (config.cookieDomain) window.cookieBanner_cookieDomain = config.cookieDomain
    if (config.cookieSecure) window.cookieBanner_cookieSecure = config.cookieSecure
    if (config.cookieExpiryDays) window.cookieBanner_cookieExpiryDays = config.cookieExpiryDays
    if (config.devMode) window.cookieBanner_devMode = config.devMode
    /* eslint-enable camelcase */

    const emitConsents = (consent: CookieConsentEvent) => {
      const handler = onToggleRef.current
      if (!handler) return
      handler(
        new CustomEvent<ConsentDetailsMap>('toggle-consent', {
          detail: toConsentDetails(consent),
          bubbles: true,
          cancelable: false,
        }),
      )
    }

    triggerInitRef.current = () => {
      window.document.dispatchEvent(new Event('CookieBannerReady', { bubbles: true, cancelable: true }))

      const banner = window.cookieBanner
      const events = window.__cookieEvents
      if (!banner || !events) return

      banner.cookieConsent.validateConsentCookie().then((hasConsent) => {
        if (cancelled || !hasConsent) return

        const cookie = banner.cookieConsent.getConsentCookie()
        window.setTimeout(() => {
          if (!cancelled) emitConsents({ value: cookie })
        }, 0)

        if (cookieEventHandler) {
          events.off('CookieManager.setCookie', cookieEventHandler)
        }
        cookieEventHandler = (consent) => emitConsents(consent)
        events.on('CookieManager.setCookie', cookieEventHandler)
      })
    }

    loadConsentScript().then(() => {
      if (!cancelled) triggerInitRef.current()
    })

    return () => {
      cancelled = true
      if (cookieEventHandler) {
        window.__cookieEvents?.off('CookieManager.setCookie', cookieEventHandler)
      }
    }
  }, [])

  const openModal = (e: ReactMouseEvent<HTMLElement>) => {
    e.preventDefault()
    if (!window.cookieBanner?.cookieConsent) {
      triggerInitRef.current()
    }
    setTimeout(() => window.cookieBanner?.openCookieModal())
  }

  // aria-haspopup: triggeren åpner consent-dialogen, den navigerer ikke
  if (triggerType === 'link') {
    return (
      <a
        {...rest}
        ref={ref as Ref<HTMLAnchorElement>}
        href="#"
        className="pkt-link"
        aria-haspopup="dialog"
        onClick={openModal}
      >
        {resolvedTriggerText}
      </a>
    )
  }
  if (triggerType === 'footerlink') {
    return (
      <a
        {...rest}
        ref={ref as Ref<HTMLAnchorElement>}
        href="#"
        className="pkt-footer__link"
        aria-haspopup="dialog"
        onClick={openModal}
      >
        {resolvedTriggerText}
      </a>
    )
  }
  if (triggerType === 'icon') {
    return (
      <PktButton
        ref={ref as Ref<HTMLButtonElement>}
        skin="tertiary"
        variant="icon-only"
        iconName="cookie"
        aria-haspopup="dialog"
        onClick={openModal}
      >
        {resolvedTriggerText}
      </PktButton>
    )
  }
  return (
    <PktButton ref={ref as Ref<HTMLButtonElement>} aria-haspopup="dialog" onClick={openModal}>
      {resolvedTriggerText}
    </PktButton>
  )
})

PktConsent.displayName = 'PktConsent'
