import http from 'node:http'

const port = Number(process.env.PORT || 7171)
const backendBaseUrl = (process.env.BACKEND_BASE_URL || 'http://safetwitch-backend:7000').replace(
  /\/+$/,
  ''
)

const jsonHeaders = {
  'Content-Type': 'application/json; charset=utf-8',
  'Access-Control-Allow-Origin': '*'
}

const emptyResult = () => ({
  channels: [],
  categories: [],
  relatedChannels: [],
  channelsWithTag: []
})

const hasAnyResults = (data) =>
  !!data &&
  (data.channels?.length ||
    data.categories?.length ||
    data.relatedChannels?.length ||
    data.channelsWithTag?.length)

const normalize = (value) =>
  String(value || '')
    .toLowerCase()
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^a-z0-9\s]/g, ' ')
    .replace(/\biv\b/g, '4')
    .replace(/\biii\b/g, '3')
    .replace(/\bii\b/g, '2')
    .replace(/\bi\b/g, '1')
    .replace(/\s+/g, ' ')
    .trim()

const withRomanNumerals = (value) =>
  String(value || '')
    .replace(/\b4\b/gi, 'IV')
    .replace(/\b3\b/gi, 'III')
    .replace(/\b2\b/gi, 'II')
    .replace(/\b1\b/gi, 'I')

const withArabicNumerals = (value) =>
  String(value || '')
    .replace(/\biv\b/gi, '4')
    .replace(/\biii\b/gi, '3')
    .replace(/\bii\b/gi, '2')
    .replace(/\bi\b/gi, '1')

const mapDiscoverCategory = (category) => ({
  name: category?.name || '',
  displayName: category?.displayName || category?.name || '',
  viewers: Number(category?.viewers || 0),
  tags: Array.isArray(category?.tags) ? category.tags : [],
  createdAt: category?.createdAt,
  image: category?.image || ''
})

const fetchBackend = async (path) => {
  const response = await fetch(`${backendBaseUrl}${path}`, {
    redirect: 'follow',
    headers: {
      Accept: 'application/json'
    }
  })

  if (!response.ok) {
    throw new Error(`Backend request failed (${response.status}) for ${path}`)
  }

  return response.json()
}

const buildFallback = async (query) => {
  const result = emptyResult()

  try {
    const channelResp = await fetchBackend(`/api/users/${encodeURIComponent(query)}`)
    if (channelResp?.status === 'ok' && channelResp.data) {
      result.channels.push(channelResp.data)
    }
  } catch {
    // Ignore fallback channel lookup failures.
  }

  try {
    const discoverResp = await fetchBackend('/api/discover')
    const categories = Array.isArray(discoverResp?.data) ? discoverResp.data : []
    const normalizedQuery = normalize(query)

    result.categories = categories
      .filter((category) => {
        const displayName = normalize(category?.displayName)
        const name = normalize(category?.name)
        return (
          displayName.includes(normalizedQuery) ||
          normalizedQuery.includes(displayName) ||
          name.includes(normalizedQuery) ||
          normalizedQuery.includes(name)
        )
      })
      .slice(0, 24)
  } catch {
    // Ignore fallback category lookup failures.
  }

  const categoryCandidates = [...new Set([query, withRomanNumerals(query), withArabicNumerals(query)])]
  for (const candidate of categoryCandidates) {
    const trimmed = candidate.trim()
    if (!trimmed) continue

    try {
      const categoryResp = await fetchBackend(`/api/discover/${encodeURIComponent(trimmed)}`)
      if (categoryResp?.status !== 'ok' || !categoryResp?.data) {
        continue
      }

      const mapped = mapDiscoverCategory(categoryResp.data)
      if (!mapped.name) {
        continue
      }

      const alreadyIncluded = result.categories.some(
        (existing) => normalize(existing.name) === normalize(mapped.name)
      )
      if (!alreadyIncluded) {
        result.categories.unshift(mapped)
      }
    } catch {
      // Ignore exact category lookup failures.
    }
  }

  return result
}

const handleSearch = async (query) => {
  if (!query) {
    return { status: 'ok', data: emptyResult() }
  }

  try {
    const backendSearch = await fetchBackend(`/api/search/?query=${encodeURIComponent(query)}`)
    if (backendSearch?.status === 'ok' && hasAnyResults(backendSearch.data)) {
      return backendSearch
    }
  } catch {
    // Fallback path below handles backend search failures.
  }

  const fallbackData = await buildFallback(query)
  return { status: 'ok', data: fallbackData }
}

const server = http.createServer(async (req, res) => {
  if (!req.url) {
    res.writeHead(400, jsonHeaders)
    res.end(JSON.stringify({ status: 'error', message: 'Missing request URL' }))
    return
  }

  if (req.method === 'OPTIONS') {
    res.writeHead(204, {
      ...jsonHeaders,
      'Access-Control-Allow-Methods': 'GET,OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type,Accept-Language'
    })
    res.end()
    return
  }

  const url = new URL(req.url, 'http://localhost')
  const query = (url.searchParams.get('query') || '').trim()

  if (!url.pathname.startsWith('/api/search')) {
    res.writeHead(404, jsonHeaders)
    res.end(JSON.stringify({ status: 'error', message: 'Not found' }))
    return
  }

  try {
    const payload = await handleSearch(query)
    res.writeHead(200, jsonHeaders)
    res.end(JSON.stringify(payload))
  } catch (error) {
    res.writeHead(500, jsonHeaders)
    res.end(JSON.stringify({ status: 'error', message: String(error) }))
  }
})

server.listen(port, '0.0.0.0')
