const storageType = 'localStorage'

let data = {}

const statusEl = document.getElementById('status')
const formEl = document.getElementById('search')

const errors = {
  missingQuery: new TypeError('Query is missing.'),
  noFoundInstances: new Error('No instances were found.'),
}

function buildSearchURL (opts = {}) {
  if (typeof opts.query !== 'string') throw errors.missingQuery
  if (!('instances' in data)) throw errors.noFoundInstances
  if (data.instances.length === 0) throw errors.noFoundInstances

  // Random instance
  const n = Math.floor(Math.random() * data.instances.length)
  const instanceURL = data.instances[n]

  // Build URL
  const params = new URLSearchParams({
    q: opts.query
  })
  if (opts.category && opts.category !== 'all') {
    params.append(`category_${opts.category}`, '')
  }
  const url = `${instanceURL}?${params.toString()}`
  return url
}

// Register event listeners
formEl.addEventListener('submit', (event) => {
  event.preventDefault()
  location.assign(buildSearchURL({
    query: formEl.query.value,
    category: formEl.category.value
  }))
})

function locallyGetInstances () {
  if (!(storageType in window)) return []
  const item = window[storageType].getItem('instances')
  try {
    return JSON.parse(item)
  } catch (_err) {
    return []
  }
}

function locallySetInstances () {
  if (!(storageType in window)) return
  if (!('instances' in data)) return
  if (data.instances.length === 0) return
  window[storageType].setItem(
    'instances',
    JSON.stringify(data.instances)
  )
}

async function remotelyGetInstances () {
  const instances = await fetch('https://searx.space/data/instances.json')
    .then((req) => req.json())
    .then((json) => Object.entries(json.instances))
    .then((entries) => entries.filter(([_url, details]) => {
      if (details.network_type !== 'normal') return
      if (details.uptime == null) return
      if (details.uptime.uptimeDay !== 100) return
      return true
    }))
    .then((entries) => entries.map(([url]) => url))
    .catch((err) => {
      console.error(err)
      return []
    })
  window[storageType].setItem('timestamp', Date.now().toString())
  return instances
}

// Get local instances
data.instances = locallyGetInstances()

// Fetch remote instances if necessary
const timestamp = +window[storageType].getItem('timestamp')
if (Date.now() > timestamp + 3_600_000 /* 1 hour */) {
  const remoteInstances = await remotelyGetInstances()
  if (remoteInstances.length > 0) {
    data.instances = remoteInstances
    locallySetInstances()
  }
}

// Auto-redirect in presence of search parameters
if (location.search.length > 0) {
  const searchParams = new URLSearchParams(location.search.slice(1))
  if (searchParams.has('q')) {
    location.assign(buildSearchURL({
      query: searchParams.get('q'),
      category: searchParams.get('in') ?? 'all'
    }))
  }
}

statusEl.textContent = `${data.instances.length} instances`
if (data.instances.length === 0) {
  statusEl.classList.add('error')
}