const YOOKASSA_API_URL = 'https://api.yookassa.ru/v3'

function getAuthHeader() {
  const shopId = process.env.YOOKASSA_SHOP_ID
  const secretKey = process.env.YOOKASSA_SECRET_KEY
  if (!shopId || !secretKey) {
    throw new Error('YOOKASSA_SHOP_ID and YOOKASSA_SECRET_KEY are required')
  }
  const token = Buffer.from(`${shopId}:${secretKey}`).toString('base64')
  return `Basic ${token}`
}

function isRetryable(status) {
  return status >= 500 || status === 429
}

async function fetchWithRetry(url, opts, maxRetries = 3) {
  let lastError
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    if (attempt > 0) {
      const delay = 500 * 2 ** (attempt - 1)
      await new Promise((resolve) => setTimeout(resolve, delay))
    }
    try {
      const res = await fetch(url, opts)
      if (res.ok) return res
      const body = await res.json().catch(() => ({}))
      if (isRetryable(res.status)) {
        lastError = new Error(`YooKassa API error: ${res.status} — ${body.description || 'unknown'}`)
        continue
      }
      throw new Error(
        `YooKassa API error: ${res.status} — ${body.description || body.code || 'unknown'} (${body.parameter || 'n/a'})`,
      )
    } catch (err) {
      if (err instanceof Error && err.message.startsWith('YooKassa API error')) throw err
      lastError = new Error(`YooKassa API error: network failure — ${err instanceof Error ? err.message : String(err)}`)
      if (attempt === maxRetries) throw lastError
    }
  }
  throw lastError
}

export async function createPayment({
  amount,
  description,
  receipt,
  confirmation,
  metadata,
  idempotencyKey,
  clientIp,
}) {
  const headers = {
    Authorization: getAuthHeader(),
    'Idempotence-Key': idempotencyKey,
    'Content-Type': 'application/json',
  }

  const body = {
    amount,
    capture: true,
    description,
    confirmation,
    metadata,
  }

  if (receipt) {
    body.receipt = receipt
  }
  if (clientIp) {
    body.client_ip = clientIp
  }

  const res = await fetchWithRetry(`${YOOKASSA_API_URL}/payments`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body),
  })

  const data = await res.json()
  return {
    paymentId: data.id,
    status: data.status,
    confirmationUrl: data.confirmation?.confirmation_url || null,
    expiresAt: data.expires_at || null,
    paid: data.paid,
    test: data.test,
  }
}

export async function getPayment(paymentId) {
  const res = await fetchWithRetry(`${YOOKASSA_API_URL}/payments/${paymentId}`, {
    headers: { Authorization: getAuthHeader() },
  })
  const data = await res.json()
  return {
    paymentId: data.id,
    status: data.status,
    confirmationUrl: data.confirmation?.confirmation_url || null,
    expiresAt: data.expires_at || null,
    paid: data.paid,
    test: data.test,
  }
}

const YOOKASSA_IP_RANGES_V4 = ['185.71.76.0/27', '185.71.77.0/27', '77.75.153.0/25', '77.75.154.128/25']

function ip4ToInt(ip) {
  return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0
}

function cidrMatch(ip, cidr) {
  const [range, bits] = cidr.split('/')
  const mask = ~(2 ** (32 - parseInt(bits, 10)) - 1) >>> 0
  const ipInt = ip4ToInt(ip)
  const rangeInt = ip4ToInt(range)
  return (ipInt & mask) === (rangeInt & mask)
}

function isYookassaIp(ip) {
  const v4 = ip.replace(/^::ffff:/, '')
  if (!/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v4)) return false
  return YOOKASSA_IP_RANGES_V4.some((cidr) => cidrMatch(v4, cidr))
}

function isTestMode() {
  return process.env.YOOKASSA_SECRET_KEY?.startsWith('test_') ?? false
}

export function validateWebhook(ip, body) {
  if (!isTestMode() && !isYookassaIp(ip)) {
    throw new Error('Invalid webhook source IP')
  }
  if (!body || typeof body !== 'object') {
    throw new Error('Invalid webhook body')
  }
  if (body.type !== 'notification') {
    throw new Error('Expected notification type in webhook body')
  }
  if (!body.event || !body.object) {
    throw new Error('Missing event or object in webhook body')
  }
  return { event: body.event, paymentObject: body.object }
}

export function buildReceipt({ orderItems, deliveryFeeCents, userEmail, taxSystemCode = 1 }) {
  const items = orderItems.map((item) => ({
    description: (item.titleSnapshot || 'Товар').slice(0, 128),
    quantity: item.qty,
    amount: {
      value: (item.priceCentsSnapshot / 100).toFixed(2),
      currency: 'RUB',
    },
    vat_code: 1,
    measure: 'piece',
    payment_subject: 'commodity',
    payment_mode: 'full_prepayment',
  }))

  if (deliveryFeeCents > 0) {
    items.push({
      description: 'Доставка',
      quantity: 1,
      amount: {
        value: (deliveryFeeCents / 100).toFixed(2),
        currency: 'RUB',
      },
      vat_code: 1,
      measure: 'piece',
      payment_subject: 'service',
      payment_mode: 'full_prepayment',
    })
  }

  const receipt = {
    customer: { email: userEmail },
    items,
    tax_system_code: taxSystemCode,
  }

  return receipt
}
