import crypto from 'crypto';

const VERSION = 1;
const VALID_DURATIONS = new Set([30, 90, 180, 365]);
const BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';

function secret() {
  const value = process.env.DASHCADDY_LICENSE_SECRET || '';
  if (!/^[A-Fa-f0-9]{32,}$/.test(value)) {
    throw new Error('DASHCADDY_LICENSE_SECRET must be a hex secret of at least 128 bits');
  }
  return value;
}

function base32Encode(buffer) {
  let bits = '';
  for (const byte of buffer) bits += byte.toString(2).padStart(8, '0');
  while (bits.length % 5 !== 0) bits += '0';
  let result = '';
  for (let i = 0; i < bits.length; i += 5) result += BASE32[parseInt(bits.slice(i, i + 5), 2)];
  return result;
}

function base32Decode(value) {
  let bits = '';
  for (const char of value.toUpperCase()) {
    const index = BASE32.indexOf(char);
    if (index < 0) throw new Error(`Invalid base32 character: ${char}`);
    bits += index.toString(2).padStart(5, '0');
  }
  const bytes = [];
  for (let i = 0; i + 8 <= bits.length; i += 8) bytes.push(parseInt(bits.slice(i, i + 8), 2));
  return Buffer.from(bytes);
}

export function generateCompatibleLicenseCode(durationDays, codeId, createdTs = Math.floor(Date.now() / 1000)) {
  if (!VALID_DURATIONS.has(durationDays)) throw new Error(`Invalid duration: ${durationDays}`);
  if (!Number.isInteger(codeId) || codeId < 1 || codeId > 0xFFFFFFFF) throw new Error('Invalid codeId');
  const payload = Buffer.alloc(10);
  payload.writeUInt16BE(((VERSION & 0x0F) << 12) | (durationDays & 0x0FFF), 0);
  payload.writeUInt32BE(codeId, 2);
  payload.writeUInt32BE(createdTs, 6);
  const signature = crypto.createHmac('sha256', secret()).update(payload).digest().subarray(0, 5);
  const encoded = base32Encode(Buffer.concat([payload, signature])).padEnd(25, '0').slice(0, 25);
  return `DC-${encoded.match(/.{5}/g).join('-')}`;
}

export function verifyCompatibleLicenseCode(code) {
  try {
    const cleaned = String(code).replace(/^DC-/, '').replace(/-/g, '');
    if (cleaned.length !== 25) return { valid: false, reason: 'Invalid code length' };
    const decoded = base32Decode(cleaned).subarray(0, 15);
    if (decoded.length < 15) return { valid: false, reason: 'Invalid code payload' };
    const payload = decoded.subarray(0, 10);
    const signature = decoded.subarray(10, 15);
    const expected = crypto.createHmac('sha256', secret()).update(payload).digest().subarray(0, 5);
    if (!crypto.timingSafeEqual(signature, expected)) return { valid: false, reason: 'Invalid signature' };
    const packed = payload.readUInt16BE(0);
    const version = (packed >> 12) & 0x0F;
    const durationDays = packed & 0x0FFF;
    const codeId = payload.readUInt32BE(2);
    const createdTs = payload.readUInt32BE(6);
    return {
      valid: version === VERSION && VALID_DURATIONS.has(durationDays),
      version,
      durationDays,
      codeId,
      createdTs,
    };
  } catch (error) {
    return { valid: false, reason: error.message };
  }
}
