// Proof-of-Work Captcha Service
// Client must find nonce where SHA256(challenge + nonce) has N leading zeros
// Server-first: tries /pow/challenge endpoint, falls back to local generation

const POW_SERVER = 'https://pow.kashilo.com'
const DIFFICULTY = 4
const SERVER_TIMEOUT_MS = 1500

const LOCAL_HMAC_KEY = 'kashilo-pow-local-v1'

async function hmacSign(message) {
  const enc = new TextEncoder()
  const key = await crypto.subtle.importKey(
    'raw', enc.encode(LOCAL_HMAC_KEY), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
  )
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(message))
  return Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('')
}

async function hmacVerify(message, signature) {
  const expected = await hmacSign(message)
  return expected === signature
}

async function localGenerateChallenge() {
  const challenge = crypto.randomUUID()
  const timestamp = Date.now()
  const signature = await hmacSign(`${challenge}:${timestamp}:${DIFFICULTY}`)
  return {
    challenge,
    difficulty: DIFFICULTY,
    timestamp,
    signature,
    source: 'local'
  }
}

export async function generateChallenge() {
  try {
    const controller = new AbortController()
    const timeout = setTimeout(() => controller.abort(), SERVER_TIMEOUT_MS)

    const response = await fetch(`${POW_SERVER}/challenge`, {
      method: 'GET',
      signal: controller.signal
    })
    clearTimeout(timeout)

    if (!response.ok) throw new Error(`HTTP ${response.status}`)

    const data = await response.json()
    return { ...data, source: 'server' }
  } catch {
    return localGenerateChallenge()
  }
}

export async function verifySolution(solution) {
  if (solution.source === 'server') {
    const controller = new AbortController()
    const timeout = setTimeout(() => controller.abort(), SERVER_TIMEOUT_MS)

    try {
      const response = await fetch(`${POW_SERVER}/verify`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(solution),
        signal: controller.signal
      })
      clearTimeout(timeout)

      if (!response.ok) throw new Error(`HTTP ${response.status}`)

      const data = await response.json()
      return { ok: data.ok === true, token: data.captcha_token || null }
    } catch {
      clearTimeout(timeout)
      return { ok: false, token: null }
    }
  }

  const maxAge = 5 * 60 * 1000
  if (!solution.timestamp || Date.now() - solution.timestamp > maxAge) {
    return { ok: false, token: null }
  }

  const sigValid = await hmacVerify(
    `${solution.challenge}:${solution.timestamp}:${solution.difficulty || DIFFICULTY}`,
    solution.signature
  )
  if (!sigValid) return { ok: false, token: null }

  const hash = await sha256(`${solution.challenge}${solution.nonce}`)
  const prefix = '0'.repeat(solution.difficulty || DIFFICULTY)
  return { ok: hash.startsWith(prefix), token: null }
}

export async function solveChallenge(challenge, difficulty, onProgress) {
  let nonce = 0
  const prefix = '0'.repeat(difficulty)
  const startTime = Date.now()

  while (true) {
    const hash = await sha256(`${challenge}${nonce}`)

    if (hash.startsWith(prefix)) {
      return {
        nonce,
        hash,
        duration: Date.now() - startTime
      }
    }

    nonce++

    if (onProgress && nonce % 1000 === 0) {
      onProgress({ attempts: nonce, elapsed: Date.now() - startTime })
    }

    if (nonce % 100 === 0) {
      await new Promise(r => setTimeout(r, 0))
    }
  }
}

async function sha256(message) {
  const msgBuffer = new TextEncoder().encode(message)
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
}
