import crypto from 'node:crypto'

export interface OAuth1Credentials {
  consumerKey: string
  consumerSecret: string
  accessToken: string
  accessTokenSecret: string
}

const enc = (s: string) => encodeURIComponent(s).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)

/**
 * OAuth 1.0a (HMAC-SHA256) Authorization header for a Magento Integration.
 * Needed on Magento ≥ 2.4.4 unless "Allow OAuth Access Tokens to be used as standalone Bearer tokens" is enabled.
 */
export function oauth1Header(method: string, url: string, creds: OAuth1Credentials, nonce?: string, timestamp?: string): string {
  const oauth: Record<string, string> = {
    oauth_consumer_key: creds.consumerKey,
    oauth_nonce: nonce ?? crypto.randomBytes(16).toString('hex'),
    oauth_signature_method: 'HMAC-SHA256',
    oauth_timestamp: timestamp ?? Math.floor(Date.now() / 1000).toString(),
    oauth_token: creds.accessToken,
    oauth_version: '1.0',
  }

  const u = new URL(url)
  const pairs: Array<[string, string]> = Object.entries(oauth)
  u.searchParams.forEach((v, k) => pairs.push([k, v]))
  const paramString = pairs
    .map(([k, v]) => [enc(k), enc(v)] as const)
    .sort(([ak, av], [bk, bv]) => (ak === bk ? (av < bv ? -1 : 1) : ak < bk ? -1 : 1))
    .map(([k, v]) => `${k}=${v}`)
    .join('&')

  const base = `${method.toUpperCase()}&${enc(`${u.protocol}//${u.host}${u.pathname}`)}&${enc(paramString)}`
  const key = `${enc(creds.consumerSecret)}&${enc(creds.accessTokenSecret)}`
  oauth.oauth_signature = crypto.createHmac('sha256', key).update(base).digest('base64')

  return 'OAuth ' + Object.keys(oauth).sort().map(k => `${enc(k)}="${enc(oauth[k]!)}"`).join(', ')
}
