import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'

const DEFAULT_TIMEOUT_MS = 10000

type BarkSendInput = {
  serverUrl?: string
  recipient?: {
    id?: string
    name?: string
    deviceKey?: string
  }
  title?: string
  body?: string
  group?: string
  url?: string
}

export async function sendBarkNotification(input: BarkSendInput) {
  const serverUrl = String(input.serverUrl || 'https://api.day.app')
    .trim()
    .replace(/\/+$/, '')
  const deviceKey = String(input.recipient?.deviceKey || '').trim()
  const title = String(input.title || '').trim()
  const body = String(input.body || '').trim()

  if (!deviceKey) {
    throw createHttpError('Bark 接收人缺少 deviceKey', {
      statusCode: 400,
      errorCode: 'bark_device_key_required',
    })
  }

  if (!title && !body) {
    throw createHttpError('Bark 通知标题或内容不能为空', {
      statusCode: 400,
      errorCode: 'bark_message_required',
    })
  }

  const endpoint = buildBarkEndpoint(serverUrl, deviceKey, title || body, body)
  const params = new URLSearchParams()
  params.set('group', String(input.group || '订单系统').trim() || '订单系统')
  if (input.url) {
    params.set('url', String(input.url).trim())
  }

  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS)

  try {
    const response = await fetch(`${endpoint}?${params.toString()}`, {
      method: 'GET',
      headers: {
        Accept: 'application/json, text/plain, */*',
      },
      signal: controller.signal,
    })
    const responseText = await response.text()
    const parsed = parseJsonResponse(responseText)

    if (!response.ok || isBarkFailure(parsed)) {
      throw createHttpError(resolveBarkErrorMessage(parsed, responseText, response.status), {
        statusCode: response.ok ? 502 : response.status,
        errorCode: 'bark_send_failed',
        context: {
          status: response.status,
          response: parsed || responseText,
        },
      })
    }

    return {
      ok: true,
      status: response.status,
      response: parsed || responseText,
    }
  } catch (error) {
    if (isAbortError(error)) {
      throw createHttpError('Bark 通知发送超时', {
        statusCode: 503,
        errorCode: 'bark_send_timeout',
        cause: error,
      })
    }
    throw error
  } finally {
    clearTimeout(timer)
  }
}

function buildBarkEndpoint(serverUrl: string, deviceKey: string, title: string, body: string) {
  const segments = [serverUrl, encodeURIComponent(deviceKey), encodeURIComponent(title)]

  if (body) {
    segments.push(encodeURIComponent(body))
  }

  return segments.join('/')
}

function parseJsonResponse(text: string) {
  if (!text) {
    return null
  }

  try {
    return JSON.parse(text)
  } catch {
    return null
  }
}

function isBarkFailure(parsed: unknown) {
  if (!isPlainObject(parsed)) {
    return false
  }

  const code = Number(parsed.code ?? parsed.status ?? 0)
  return Number.isFinite(code) && code !== 0 && code !== 200
}

function resolveBarkErrorMessage(parsed: unknown, responseText: string, status: number) {
  if (isPlainObject(parsed)) {
    return (
      String(parsed.message || parsed.msg || parsed.error || '').trim() ||
      `Bark 通知发送失败，HTTP ${status}`
    )
  }

  return String(responseText || '').trim() || `Bark 通知发送失败，HTTP ${status}`
}

function isAbortError(error: unknown): error is Error {
  return error instanceof Error && error.name === 'AbortError'
}

function isPlainObject(value: unknown): value is JsonObject {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}
