import type { AsukaLicenseCompactHeader, AsukaLicenseEntitlements, AsukaSignedLicensePayload } from './license.types';

function getNodeBuffer(): any | null {
  try {
    return (globalThis as any)?.Buffer ?? null;
  } catch {
    return null;
  }
}

export function stableSerialize(value: unknown): string {
  if (value === null || typeof value !== 'object') {
    return JSON.stringify(value);
  }
  if (Array.isArray(value)) {
    return `[${value.map((entry) => stableSerialize(entry)).join(',')}]`;
  }
  const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b));
  return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableSerialize(val)}`).join(',')}}`;
}

export function decodeBase64ToUint8Array(input: string): Uint8Array {
  const normalized = String(input ?? '').trim();
  if (!normalized) return new Uint8Array();
  if (typeof atob === 'function') {
    const decoded = atob(normalized);
    return Uint8Array.from(decoded, (char) => char.charCodeAt(0));
  }
  const BufferCtor = getNodeBuffer();
  if (BufferCtor) {
    return new Uint8Array(BufferCtor.from(normalized, 'base64'));
  }
  return new Uint8Array();
}

export function base64UrlToBase64(input: string): string {
  const normalized = String(input ?? '').trim().replace(/-/g, '+').replace(/_/g, '/');
  if (!normalized) return '';
  const padding = normalized.length % 4;
  if (padding === 2) return `${normalized}==`;
  if (padding === 3) return `${normalized}=`;
  if (padding === 1) return `${normalized}===`;
  return normalized;
}

export function decodeBase64UrlToUint8Array(input: string): Uint8Array {
  return decodeBase64ToUint8Array(base64UrlToBase64(input));
}

export function decodeBase64UrlJson<T = unknown>(input: string): T {
  const bytes = decodeBase64UrlToUint8Array(input);
  const text = new TextDecoder().decode(bytes);
  return JSON.parse(text) as T;
}

function isCompactTokenString(value: unknown): value is string {
  return typeof value === 'string' && value.trim().split('.').length === 3;
}

export function parseCompactLicenseToken(token: string): AsukaSignedLicensePayload {
  if (!isCompactTokenString(token)) {
    throw new Error('Invalid compact license token');
  }

  const [encodedHeader, encodedPayload, encodedSignature] = token.trim().split('.');
  const header = decodeBase64UrlJson<AsukaLicenseCompactHeader>(encodedHeader);
  const payload = decodeBase64UrlJson<AsukaLicenseEntitlements>(encodedPayload);

  return {
    payload,
    signature: encodedSignature,
    alg: String(header?.alg ?? 'EdDSA'),
    kid: typeof header?.kid === 'string' ? header.kid : undefined,
    header,
    compact: token.trim(),
    signingInput: `${encodedHeader}.${encodedPayload}`,
    format: 'compact',
  };
}

export function normalizeSignedLicenseInput(input: AsukaSignedLicensePayload | string): AsukaSignedLicensePayload {
  if (typeof input === 'string') {
    return parseCompactLicenseToken(input);
  }
  return input;
}
