'use client'

import { useEffect } from 'react'

const allowedHosts = new Set(['www.eversolo.com', 'eversolo.com'])
const proxyPath = '/api/eversolo-paste-url'
const pasteFetchCollectDelay = 300
const pasteFetchIdleReleaseDelay = 250
const pasteFetchRetryDelays = [0, 300, 800]
const pasteFetchConcurrency = 4
const pasteFetchPatchVersion = 5
const bypassPathPrefixes = ['/admin', '/api', '/_next']
const assetPathPrefixes = ['/Attached/', '/Products/', '/Uploads/', '/controlapp/', '/files/', '/media/', '/videos/']
const assetExtensions = new Set([
  '7z',
  'aac',
  'avi',
  'bmp',
  'css',
  'csv',
  'doc',
  'docx',
  'flac',
  'gif',
  'gz',
  'htm',
  'html',
  'ico',
  'jpeg',
  'jpg',
  'js',
  'json',
  'm4a',
  'mov',
  'mp3',
  'mp4',
  'pdf',
  'png',
  'rar',
  'svg',
  'tar',
  'txt',
  'wav',
  'webm',
  'webp',
  'xls',
  'xlsx',
  'xml',
  'zip',
])

type PatchedWindow = Window & {
  __eversoloPasteURLFetchPatchVersion?: number
}

type QueuedPasteFetch = {
  init?: RequestInit
  proxiedURL: string
  reject: (reason?: unknown) => void
  resolve: (response: Response) => void
  result?: PromiseSettledResult<Response>
  source?: QueuedPasteFetch
}

let activePasteFetches = 0
let pasteFetchCollectTimer: number | undefined
let pasteFetchReleaseTimer: number | undefined
const pasteFetchPendingQueue: QueuedPasteFetch[] = []
let pasteFetchSession: QueuedPasteFetch[] = []

function getFetchURL(input: RequestInfo | URL) {
  if (typeof input === 'string') return input
  if (input instanceof URL) return input.href

  return input.url
}

function getFetchMethod(input: RequestInfo | URL, init?: RequestInit) {
  if (init?.method) return init.method
  if (typeof input !== 'string' && !(input instanceof URL)) return input.method

  return undefined
}

function isBypassedURL(url: URL) {
  if (url.origin === window.location.origin) return true
  if (url.searchParams.has('_rsc')) return true

  return bypassPathPrefixes.some((prefix) => {
    return url.pathname === prefix || url.pathname.startsWith(`${prefix}/`)
  })
}

function isAssetURL(url: URL) {
  if (assetPathPrefixes.some((prefix) => url.pathname.startsWith(prefix))) return true

  const extension = url.pathname.split('/').pop()?.split('.').pop()?.toLowerCase()

  return extension ? assetExtensions.has(extension) : false
}

function getProxiedPasteURL(input: RequestInfo | URL, init?: RequestInit) {
  const method = getFetchMethod(input, init)

  if (method && method.toUpperCase() !== 'GET' && method.toUpperCase() !== 'HEAD') {
    return null
  }

  try {
    const url = new URL(getFetchURL(input))

    if (url.protocol !== 'https:' || !allowedHosts.has(url.hostname)) {
      return null
    }

    if (isBypassedURL(url) || !isAssetURL(url)) {
      return null
    }

    return `${proxyPath}?src=${encodeURIComponent(url.href)}`
  } catch {
    return null
  }
}

function wait(delay: number) {
  return new Promise((resolve) => {
    window.setTimeout(resolve, delay)
  })
}

async function fetchWithRetry(
  originalFetch: typeof fetch,
  proxiedURL: string,
  init?: RequestInit,
) {
  let lastError: unknown

  for (const [index, delay] of pasteFetchRetryDelays.entries()) {
    if (delay > 0) {
      await wait(delay)
    }

    try {
      const response = await originalFetch(proxiedURL, {
        ...init,
        method: 'GET',
      })

      if (response.ok || index === pasteFetchRetryDelays.length - 1) {
        return response
      }
    } catch (error) {
      lastError = error
    }
  }

  throw lastError
}

function releasePasteFetchSession() {
  if (activePasteFetches > 0 || pasteFetchPendingQueue.length > 0) return

  const session = pasteFetchSession

  pasteFetchSession = []
  pasteFetchReleaseTimer = undefined

  session.forEach((item) => {
    const result = item.result ?? item.source?.result

    if (!result) return

    if (result.status === 'fulfilled') {
      item.resolve(result.value.clone())
    } else {
      item.reject(result.reason)
    }
  })
}

function schedulePasteFetchRelease() {
  if (activePasteFetches > 0 || pasteFetchPendingQueue.length > 0) return

  if (pasteFetchReleaseTimer) {
    window.clearTimeout(pasteFetchReleaseTimer)
  }

  pasteFetchReleaseTimer = window.setTimeout(() => {
    releasePasteFetchSession()
  }, pasteFetchIdleReleaseDelay)
}

function processPasteFetchQueue(originalFetch: typeof fetch) {
  pasteFetchCollectTimer = undefined

  while (activePasteFetches < pasteFetchConcurrency && pasteFetchPendingQueue.length > 0) {
    const item = pasteFetchPendingQueue.shift()

    if (!item) continue

    activePasteFetches += 1
    fetchWithRetry(originalFetch, item.proxiedURL, item.init)
      .then((response) => {
        item.result = {
          status: 'fulfilled',
          value: response,
        }
      })
      .catch((reason: unknown) => {
        item.result = {
          reason,
          status: 'rejected',
        }
      })
      .finally(() => {
        activePasteFetches -= 1
        processPasteFetchQueue(originalFetch)
        schedulePasteFetchRelease()
      })
  }

  schedulePasteFetchRelease()
}

function enqueuePasteFetch(originalFetch: typeof fetch, proxiedURL: string, init?: RequestInit) {
  return new Promise<Response>((resolve, reject) => {
    if (pasteFetchReleaseTimer) {
      window.clearTimeout(pasteFetchReleaseTimer)
      pasteFetchReleaseTimer = undefined
    }

    const source = pasteFetchSession.find((pendingItem) => {
      return !pendingItem.source && pendingItem.proxiedURL === proxiedURL
    })
    const item: QueuedPasteFetch = {
      init,
      proxiedURL,
      reject,
      resolve,
      source,
    }

    pasteFetchSession.push(item)

    if (source) {
      schedulePasteFetchRelease()
      return
    }

    pasteFetchPendingQueue.push(item)

    if (pasteFetchCollectTimer) {
      window.clearTimeout(pasteFetchCollectTimer)
    }

    pasteFetchCollectTimer = window.setTimeout(() => {
      processPasteFetchQueue(originalFetch)
    }, pasteFetchCollectDelay)
  })
}

export function EversoloPasteURLFetchProvider({ children }: { children?: React.ReactNode }) {
  useEffect(() => {
    const patchedWindow = window as PatchedWindow

    if (patchedWindow.__eversoloPasteURLFetchPatchVersion === pasteFetchPatchVersion) return

    const originalFetch = window.fetch.bind(window)

    patchedWindow.__eversoloPasteURLFetchPatchVersion = pasteFetchPatchVersion
    window.fetch = (input, init) => {
      const proxiedPasteURL = getProxiedPasteURL(input, init)

      if (proxiedPasteURL) {
        return enqueuePasteFetch(originalFetch, proxiedPasteURL, init)
      }

      return originalFetch(input, init)
    }
  }, [])

  return children
}
