export interface PortalClaimState {
  clientId?: string;
  tenantId?: string;
  subscriptionId?: string;
  roles: string[];
}

type JsonRecord = Record<string, unknown>;

function decodeBase64Url(value: string): string {
  const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
  const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');
  return Buffer.from(padded, 'base64').toString('utf8');
}

export function decodeJwtPayload(token?: string | null): JsonRecord {
  if (!token) return {};

  const [, payload] = token.split('.');
  if (!payload) return {};

  try {
    const decoded = decodeBase64Url(payload);
    const parsed = JSON.parse(decoded);
    return parsed && typeof parsed === 'object' ? (parsed as JsonRecord) : {};
  } catch {
    return {};
  }
}

function readStringClaim(source: JsonRecord, keys: string[]): string | undefined {
  for (const key of keys) {
    const value = source[key];
    if (typeof value === 'string' && value.trim() !== '') return value.trim();
  }
  return undefined;
}

function readStringArrayClaim(source: JsonRecord, keys: string[]): string[] {
  for (const key of keys) {
    const value = source[key];
    if (Array.isArray(value)) {
      const strings = value.filter((item): item is string => typeof item === 'string' && item.trim() !== '');
      if (strings.length > 0) return strings;
    }
  }
  return [];
}

function readRealmRoles(source: JsonRecord): string[] {
  const realmAccess = source.realm_access;
  if (!realmAccess || typeof realmAccess !== 'object') return [];

  const roles = (realmAccess as JsonRecord).roles;
  if (!Array.isArray(roles)) return [];

  return roles.filter((item): item is string => typeof item === 'string' && item.trim() !== '');
}

export function extractPortalClaimState(...sources: JsonRecord[]): PortalClaimState {
  const clientId =
    sources.map((source) => readStringClaim(source, ['clientId', 'client_id', 'client'])).find(Boolean) ||
    undefined;
  const tenantId =
    sources.map((source) => readStringClaim(source, ['tenantId', 'tenant_id', 'tenant'])).find(Boolean) ||
    undefined;
  const subscriptionId =
    sources
      .map((source) => readStringClaim(source, ['subscriptionId', 'subscription_id', 'subscription']))
      .find(Boolean) || undefined;

  const roleSet = new Set<string>();
  for (const source of sources) {
    for (const role of readRealmRoles(source)) roleSet.add(role);
    for (const role of readStringArrayClaim(source, ['roles', 'groups'])) roleSet.add(role);
    const directRole = readStringClaim(source, ['role']);
    if (directRole) roleSet.add(directRole);
  }

  return {
    clientId,
    tenantId,
    subscriptionId,
    roles: Array.from(roleSet),
  };
}
