import { createHmac, randomInt, randomUUID } from 'node:crypto'
import './env.mjs'

const defaultEndpoint = 'https://dysmsapi.aliyuncs.com/'

function envFlag(name, defaultValue = false) {
  const value = process.env[name]
  if (value === undefined || value === null || value === '') return defaultValue
  return ['1', 'true', 'yes', 'on'].includes(String(value).trim().toLowerCase())
}

function smsConfig() {
  return {
    enabled: envFlag('ENABLE_ALIBABA_CLOUD_SMS', true),
    accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID || '',
    accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET || '',
    signName: process.env.ALIBABA_CLOUD_SMS_SIGN_NAME || '',
    templateCode: process.env.ALIBABA_CLOUD_SMS_TEMPLATE_CODE || '',
    endpoint: process.env.ALIBABA_CLOUD_SMS_ENDPOINT || defaultEndpoint,
    regionId: process.env.ALIBABA_CLOUD_SMS_REGION_ID || 'cn-hangzhou',
    timeoutMs: Math.max(1000, Number(process.env.ALIBABA_CLOUD_SMS_TIMEOUT_MS || 8000)),
  }
}

export function smsProviderConfigured() {
  const config = smsConfig()
  return Boolean(config.enabled && config.accessKeyId && config.accessKeySecret && config.signName && config.templateCode)
}

export function generateSmsCode() {
  return String(randomInt(100000, 1000000))
}

function percentEncode(value) {
  return encodeURIComponent(String(value))
    .replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`)
}

function buildSignature(params, accessKeySecret) {
  const canonicalized = Object.keys(params)
    .sort()
    .map((key) => `${percentEncode(key)}=${percentEncode(params[key])}`)
    .join('&')
  const stringToSign = `POST&%2F&${percentEncode(canonicalized)}`
  return createHmac('sha1', `${accessKeySecret}&`).update(stringToSign).digest('base64')
}

function timestamp() {
  return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z')
}

function providerError(message, code = 'sms_send_failed', statusCode = 502, details = {}) {
  const error = new Error(message)
  error.statusCode = statusCode
  error.code = code
  error.details = details
  return error
}

export async function sendAliyunSmsCode(phone, code) {
  const config = smsConfig()
  if (!smsProviderConfigured()) {
    throw providerError('短信服务配置不完整', 'sms_provider_not_configured', 503)
  }

  const params = {
    AccessKeyId: config.accessKeyId,
    Action: 'SendSms',
    Format: 'JSON',
    PhoneNumbers: phone,
    RegionId: config.regionId,
    SignName: config.signName,
    SignatureMethod: 'HMAC-SHA1',
    SignatureNonce: randomUUID(),
    SignatureVersion: '1.0',
    TemplateCode: config.templateCode,
    TemplateParam: JSON.stringify({ code }),
    Timestamp: timestamp(),
    Version: '2017-05-25',
  }
  const body = new URLSearchParams({
    ...params,
    Signature: buildSignature(params, config.accessKeySecret),
  })

  let response
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), config.timeoutMs)
  try {
    response = await fetch(config.endpoint, {
      method: 'POST',
      headers: { 'content-type': 'application/x-www-form-urlencoded' },
      body,
      signal: controller.signal,
    })
  } catch (error) {
    const timedOut = error.name === 'AbortError'
    throw providerError(timedOut ? '短信网关请求超时' : '短信网关连接失败', timedOut ? 'sms_gateway_timeout' : 'sms_gateway_unreachable', 502, { cause: error.message })
  } finally {
    clearTimeout(timer)
  }

  const text = await response.text()
  let payload
  try {
    payload = text ? JSON.parse(text) : {}
  } catch {
    throw providerError('短信网关返回异常', 'sms_gateway_bad_response', 502, { status: response.status })
  }

  if (!response.ok || payload.Code !== 'OK') {
    throw providerError(payload.Message || '短信发送失败', 'sms_send_failed', 502, {
      providerCode: payload.Code || '',
      requestId: payload.RequestId || '',
    })
  }

  return {
    requestId: payload.RequestId || '',
    bizId: payload.BizId || '',
  }
}
