const LAYER_RELATION_ALLOWLISTS = {
  token_projection: [
    'workflow_handoff',
    'allowed_next_action',
    'narrowed_audience',
    'effective_subject',
    'actor_chain_ref',
    'delegated_authority'
  ],
  workflow_overlay: [
    'workflow_step',
    'workflow_resume_state',
    'substep_orchestration',
    'responsibility_hint',
    'delegated_authority'
  ],
  request_observed: [
    'from_ip',
    'request_has_id',
    'request_has_timestamp',
    'request_has_path_method',
    'gateway_context_ref'
  ],
  attested_context: [
    'attested_device_posture_ref',
    'signed_environment_claim',
    'verified_upstream_context_ref'
  ],
  caller_declared: [
    'client_fingerprint_evidence',
    'caller_risk_hint',
    'caller_geo_hint',
    'client_ux_hint'
  ],
  challenge_evidence: [
    'challenge_satisfied',
    'approval_granted_for_step',
    'challenge_assurance_level_ref',
    'remediation_fulfillment_ref'
  ],
  provenance_overlay: [
    'actor_chain_ref',
    'intent_chain_ref',
    'inference_chain_ref',
    'content_provenance_ref',
    'evidence_hash_ref',
    'provenance_hash_ref'
  ],
  // ADR-052: Delegation evidence layers
  delegation_evidence: [
    'drt_valid',
    'drt_authorized_triple',
    'delegation_chain_depth',
    'delegated_authority'
  ],
  approval_evidence: [
    'approval_granted',
    'approval_denied',
    'drt_revoked',
    'approval_ticket_ref'
  ]
};

const LAYER_ALIASES = {
  workflow_claims: 'workflow_overlay'
};

const RELATION_CATEGORIES = {
  workflow_handoff: 'workflow_authority',
  allowed_next_action: 'workflow_authority',
  narrowed_audience: 'workflow_authority',
  effective_subject: 'workflow_authority',
  delegated_authority: 'workflow_authority',

  workflow_step: 'workflow_state',
  workflow_resume_state: 'workflow_state',
  substep_orchestration: 'workflow_state',
  responsibility_hint: 'workflow_state',

  from_ip: 'request_metadata',
  request_has_id: 'request_metadata',
  request_has_timestamp: 'request_metadata',
  request_has_path_method: 'request_metadata',
  gateway_context_ref: 'request_metadata',

  attested_device_posture_ref: 'attestation',
  signed_environment_claim: 'attestation',
  verified_upstream_context_ref: 'attestation',

  client_fingerprint_evidence: 'caller_hint',
  caller_risk_hint: 'caller_hint',
  caller_geo_hint: 'caller_hint',
  client_ux_hint: 'caller_hint',

  challenge_satisfied: 'challenge_result',
  approval_granted_for_step: 'challenge_result',
  challenge_assurance_level_ref: 'challenge_result',
  remediation_fulfillment_ref: 'challenge_result',

  // ADR-052 delegation evidence
  drt_valid: 'delegation_evidence',
  drt_authorized_triple: 'delegation_evidence',
  delegation_chain_depth: 'delegation_evidence',
  // delegated_authority category is 'workflow_authority' (set above) — shared across
  // token_projection, workflow_overlay, and delegation_evidence layers.

  // ADR-052 approval evidence
  approval_granted: 'approval_evidence',
  approval_denied: 'approval_evidence',
  drt_revoked: 'approval_evidence',
  approval_ticket_ref: 'approval_evidence',

  actor_chain_ref: 'provenance_reference',
  intent_chain_ref: 'provenance_reference',
  inference_chain_ref: 'provenance_reference',
  content_provenance_ref: 'provenance_reference',
  evidence_hash_ref: 'provenance_reference',
  provenance_hash_ref: 'provenance_reference'
};

const LAYER_ALLOWED_CATEGORIES = {
  token_projection: ['workflow_authority', 'provenance_reference'],
  workflow_overlay: ['workflow_state', 'workflow_authority'],
  request_observed: ['request_metadata'],
  attested_context: ['attestation'],
  caller_declared: ['caller_hint'],
  challenge_evidence: ['challenge_result'],
  provenance_overlay: ['provenance_reference'],
  // ADR-052 delegation layers
  delegation_evidence: ['delegation_evidence', 'workflow_authority'],
  approval_evidence: ['approval_evidence']
};

const LAYER_PROHIBITED_CATEGORIES = {
  request_observed: ['workflow_authority'],
  caller_declared: ['workflow_authority', 'workflow_state'],
  provenance_overlay: ['workflow_authority']
};

const RELATION_REDUCER_SUPPORT = {
  from_ip: ['first', 'unique', 'ignore_conflict'],
  request_has_id: ['first', 'unique', 'ignore_conflict'],
  request_has_timestamp: ['latest', 'first', 'unique'],
  caller_risk_hint: ['strongest', 'weakest', 'first'],
  caller_geo_hint: ['first', 'unique'],
  client_fingerprint_evidence: ['latest', 'strongest', 'first'],
  challenge_assurance_level_ref: ['strongest', 'weakest', 'latest'],
  actor_chain_ref: ['union_refs', 'dedupe', 'first'],
  intent_chain_ref: ['union_refs', 'dedupe', 'first'],
  inference_chain_ref: ['union_refs', 'dedupe', 'first'],
  content_provenance_ref: ['union_refs', 'dedupe', 'first'],
  evidence_hash_ref: ['union_refs', 'dedupe', 'first'],
  provenance_hash_ref: ['union_refs', 'dedupe', 'first']
};

const LAYER_NAMES = Object.keys(LAYER_RELATION_ALLOWLISTS);
const LAYER_RELATION_LOOKUPS = Object.fromEntries(
  LAYER_NAMES.map((layerName) => [
    layerName,
    Object.fromEntries(LAYER_RELATION_ALLOWLISTS[layerName].map((relation) => [relation, true]))
  ])
);
const LAYER_CONFORMANCE_MATRIX = {
  layer_count: LAYER_NAMES.length,
  layers: LAYER_NAMES.map((layerName) => ({
    layer_name: layerName,
    allowed_relations: [...LAYER_RELATION_ALLOWLISTS[layerName]],
    allowed_categories: [...(LAYER_ALLOWED_CATEGORIES[layerName] || [])],
    prohibited_categories: [...(LAYER_PROHIBITED_CATEGORIES[layerName] || [])],
    conformance: 'enforced'
  }))
};

const LAYER_ALLOWED_CATEGORY_LOOKUPS = Object.fromEntries(
  LAYER_NAMES.map((layerName) => [
    layerName,
    Object.fromEntries((LAYER_ALLOWED_CATEGORIES[layerName] || []).map((category) => [category, true]))
  ])
);

const LAYER_PROHIBITED_CATEGORY_LOOKUPS = Object.fromEntries(
  LAYER_NAMES.map((layerName) => [
    layerName,
    Object.fromEntries((LAYER_PROHIBITED_CATEGORIES[layerName] || []).map((category) => [category, true]))
  ])
);

export function resolveLayerName(layerType) {
  const raw = String(layerType || '').trim().toLowerCase();
  if (!raw) return null;
  return LAYER_ALIASES[raw] || raw;
}

export function listLayerNames() {
  return LAYER_NAMES;
}

export function getAllowedRelationsForLayer(layerName) {
  return LAYER_RELATION_ALLOWLISTS[layerName] || null;
}

export function getAllowedCategoriesForLayer(layerName) {
  return LAYER_ALLOWED_CATEGORIES[layerName] || [];
}

export function getProhibitedCategoriesForLayer(layerName) {
  return LAYER_PROHIBITED_CATEGORIES[layerName] || [];
}

export function getRelationCategory(relationName) {
  return RELATION_CATEGORIES[relationName] || 'unclassified';
}

export function buildLayerConformanceMatrix() {
  return LAYER_CONFORMANCE_MATRIX;
}

export function getSupportedReducersForRelation(relationName) {
  return RELATION_REDUCER_SUPPORT[relationName] || [];
}

export function isReducerAllowedForRelation(relationName, reducerName) {
  if (!relationName || !reducerName) return false;
  const supported = getSupportedReducersForRelation(relationName);
  for (let i = 0; i < supported.length; i++) {
    if (supported[i] === reducerName) return true;
  }
  return false;
}

export function validateReducerConfig(reducerConfig) {
  const config = reducerConfig && typeof reducerConfig === 'object' ? reducerConfig : {};
  const valid = {};
  const invalid = [];
  const keys = Object.keys(config);
  for (let i = 0; i < keys.length; i++) {
    const relation = keys[i];
    const reducer = config[relation];
    if (isReducerAllowedForRelation(relation, reducer)) {
      valid[relation] = reducer;
    } else {
      invalid.push({ relation, reducer, allowed: getSupportedReducersForRelation(relation) });
    }
  }

  return {
    valid,
    invalid,
    ok: invalid.length === 0
  };
}

export function validateClaimsForLayer(layerName, claims, allowlistRelations = null) {
  const layerAllowed = getAllowedRelationsForLayer(layerName);
  if (!layerAllowed) {
    return {
      valid: false,
      error: 'invalid_layer_type',
      allowed_layers: listLayerNames(),
      accepted_claims: [],
      rejected_claims: []
    };
  }

  let callerAllowlist = null;
  if (Array.isArray(allowlistRelations) && allowlistRelations.length > 0) {
    callerAllowlist = {};
    for (let i = 0; i < allowlistRelations.length; i++) {
      callerAllowlist[allowlistRelations[i]] = true;
    }
  }
  const allowedSet = LAYER_RELATION_LOOKUPS[layerName] || {};
  const acceptedClaims = [];
  const rejectedClaims = [];
  const disallowedRelations = [];
  const disallowedLookup = {};
  const categoryConflicts = [];
  const allowedCategories = getAllowedCategoriesForLayer(layerName);
  const prohibitedCategories = getProhibitedCategoriesForLayer(layerName);
  const allowedCategoryLookup = LAYER_ALLOWED_CATEGORY_LOOKUPS[layerName] || null;
  const prohibitedCategoryLookup = LAYER_PROHIBITED_CATEGORY_LOOKUPS[layerName] || null;

  const claimList = Array.isArray(claims) ? claims : [];
  for (let i = 0; i < claimList.length; i++) {
    const claim = claimList[i];
    if (!claim?.relation || !claim?.object) continue;
    const category = getRelationCategory(claim.relation);
    const inLayerAllowlist = allowedSet[claim.relation] === true;
    const inCallerAllowlist = !callerAllowlist || callerAllowlist[claim.relation] === true;
    const inAllowedCategory = !allowedCategoryLookup || allowedCategoryLookup[category] === true;
    const inProhibitedCategory = !!(prohibitedCategoryLookup && prohibitedCategoryLookup[category] === true);
    if (inLayerAllowlist && inCallerAllowlist && inAllowedCategory && !inProhibitedCategory) {
      acceptedClaims.push(claim);
      continue;
    }
    rejectedClaims.push(claim);
    if ((inProhibitedCategory || !inAllowedCategory) && category) {
      categoryConflicts.push({ relation: claim.relation, category, layer: layerName });
    }
    if (!disallowedLookup[claim.relation]) {
      disallowedLookup[claim.relation] = true;
      disallowedRelations.push(claim.relation);
    }
  }

  return {
    valid: rejectedClaims.length === 0,
    accepted_claims: acceptedClaims,
    rejected_claims: rejectedClaims,
    disallowed_relations: disallowedRelations,
    layer_allowed_relations: layerAllowed,
    layer_allowed_categories: allowedCategories,
    layer_prohibited_categories: prohibitedCategories,
    category_conflicts: categoryConflicts
  };
}
