'use strict';

const crypto = require('crypto');
const eventBuilders = require('../../agent/softrestaurant-bridge/event-builders');
const { assertSafe, sanitize } = require('../../agent/softrestaurant-bridge/security/payload-security');

function hash(value) {
  return crypto.createHash('sha256').update(String(value)).digest('hex');
}

const REQUIRED_FIELDS_BY_EVENT = Object.freeze({
  OPERATOR_USER_UPSERTED: ['operator_user_id', 'display_name', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_ROLE_UPSERTED: ['role_id', 'name', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_PERMISSION_UPSERTED: ['permission_id', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_ASSIGNED_TO_BRANCH: ['operator_user_id', 'branch_id', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_REMOVED_FROM_BRANCH: ['operator_user_id', 'branch_id', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_ACCOUNT_SUSPENDED: ['operator_user_id', 'scope', 'actor_id', 'reason', 'occurred_at'],
  OPERATOR_ACCOUNT_REACTIVATED: ['operator_user_id', 'scope', 'actor_id', 'reason', 'occurred_at', 'approval_request_id'],
  OPERATOR_TERMINATED: ['operator_user_id', 'scope', 'actor_id', 'reason', 'occurred_at', 'approval_request_id'],
  OPERATOR_PASSWORD_CHANGE_REQUESTED: ['operator_user_id', 'requested_by', 'reason', 'requested_at'],
  OPERATOR_PIN_RESET_REQUESTED: ['operator_user_id', 'requested_by', 'reason', 'requested_at'],
  WASTE_REASON_UPSERTED: ['reason_id', 'description', 'actor_id', 'occurred_at'],
  WASTE_RECORDED: ['waste_id', 'reason_id', 'quantity', 'estimated_cost', 'operator_user_id', 'warehouse_id', 'occurred_at'],
  INVENTORY_ADJUSTMENT_REASON_UPSERTED: ['reason_code', 'description', 'actor_id', 'occurred_at'],
  APPROVAL_REQUESTED: ['approval_request_id', 'action_type', 'requested_by', 'requested_at', 'reason'],
  APPROVAL_APPROVED: ['approval_request_id', 'decided_by', 'decided_at', 'reason'],
  APPROVAL_REJECTED: ['approval_request_id', 'decided_by', 'decided_at', 'reason'],
});

function validateCanonical(eventType, canonical) {
  const missing = (REQUIRED_FIELDS_BY_EVENT[eventType] || [])
    .filter((field) => canonical?.[field] === undefined || canonical?.[field] === null || canonical?.[field] === '');
  if (missing.length) throw new Error(`${eventType} requiere: ${missing.join(', ')}`);
  if (eventType === 'INVENTORY_MOVEMENT_RECORDED' && canonical?.movement_type === 'inventory_adjustment') {
    const adjustmentMissing = ['adjustment_id', 'reason_id', 'actor_id', 'warehouse_id', 'occurred_at']
      .filter((field) => canonical?.[field] === undefined || canonical?.[field] === null || canonical?.[field] === '');
    if (adjustmentMissing.length) throw new Error(`${eventType} requiere: ${adjustmentMissing.join(', ')}`);
  }
  if (eventType === 'WASTE_RECORDED' && (!(Number(canonical.quantity) > 0) || Number(canonical.estimated_cost) < 0)) {
    throw new Error('WASTE_RECORDED requiere quantity positiva y estimated_cost no negativo');
  }
}

function administrativeEvent(eventType, entityType, entityId, canonical, context = {}) {
  assertSafe(canonical);
  validateCanonical(eventType, canonical);
  const sourceUpdatedAt = canonical.occurred_at || canonical.requested_at || canonical.decided_at || context.createdAt || new Date().toISOString();
  const normalized = {
    entity_type: entityType,
    entity_id: `${entityType}:${hash(entityId)}`,
    canonical: sanitize(canonical),
    source: {
      system: 'arkhon-admin',
      table: 'administrative_request',
      source_id: String(entityId),
      updated_at: sourceUpdatedAt,
    },
    metadata: {
      scope: context.distributionScope || 'branch',
      target_branch_ids: context.targetBranchIds || [],
    },
  };
  return eventBuilders.buildEvent(eventType, normalized, {
    ...context,
    metadata: { ...(context.metadata || {}), target_branch_ids: context.targetBranchIds || [] },
  });
}

function accountStatusContext(canonical, context = {}) {
  if (canonical.scope === 'tenant') return { ...context, distributionScope: 'tenant', targetBranchIds: [] };
  const targetBranchIds = context.targetBranchIds || canonical.branch_ids || [canonical.branch_id].filter(Boolean);
  if (!targetBranchIds.length) throw new Error('Cambio de estado por sucursal requiere branch_id o branch_ids');
  return { ...context, distributionScope: 'explicit_branches', targetBranchIds };
}

function tenantOrExplicitContext(context = {}) {
  if (context.distributionScope) return context;
  return Array.isArray(context.targetBranchIds) && context.targetBranchIds.length
    ? { ...context, distributionScope: 'explicit_branches' }
    : { ...context, distributionScope: 'tenant' };
}

const builders = {
  createOperatorUser: (canonical, context) => administrativeEvent('OPERATOR_USER_UPSERTED', 'operator_user', canonical.operator_user_id, canonical, { ...context, distributionScope: 'tenant' }),
  upsertRole: (canonical, context) => administrativeEvent('OPERATOR_ROLE_UPSERTED', 'operator_role', canonical.role_id, canonical, tenantOrExplicitContext(context)),
  upsertPermission: (canonical, context) => administrativeEvent('OPERATOR_PERMISSION_UPSERTED', 'operator_permission', canonical.permission_id, canonical, tenantOrExplicitContext(context)),
  assignOperator: (canonical, context) => administrativeEvent('OPERATOR_ASSIGNED_TO_BRANCH', 'operator_assignment', `${canonical.operator_user_id}:${canonical.branch_id}`, canonical, { ...context, distributionScope: 'explicit_branches', targetBranchIds: [canonical.branch_id] }),
  removeOperator: (canonical, context) => administrativeEvent('OPERATOR_REMOVED_FROM_BRANCH', 'operator_assignment', `${canonical.operator_user_id}:${canonical.branch_id}`, canonical, { ...context, distributionScope: 'explicit_branches', targetBranchIds: [canonical.branch_id] }),
  suspendOperator: (canonical, context) => administrativeEvent('OPERATOR_ACCOUNT_SUSPENDED', 'suspension_record', `${canonical.operator_user_id}:${canonical.occurred_at}`, canonical, accountStatusContext(canonical, context)),
  reactivateOperator: (canonical, context) => administrativeEvent('OPERATOR_ACCOUNT_REACTIVATED', 'account_status_action', `${canonical.operator_user_id}:${canonical.occurred_at}`, canonical, accountStatusContext(canonical, context)),
  terminateOperator: (canonical, context) => administrativeEvent('OPERATOR_TERMINATED', 'termination_record', `${canonical.operator_user_id}:${canonical.occurred_at}`, canonical, { ...context, distributionScope: 'tenant' }),
  requestPasswordChange: (canonical, context) => administrativeEvent('OPERATOR_PASSWORD_CHANGE_REQUESTED', 'password_change_request', `${canonical.operator_user_id}:${canonical.requested_at}`, canonical, context),
  requestPinReset: (canonical, context) => administrativeEvent('OPERATOR_PIN_RESET_REQUESTED', 'password_change_request', `${canonical.operator_user_id}:${canonical.requested_at}`, canonical, context),
  upsertWasteReason: (canonical, context) => administrativeEvent('WASTE_REASON_UPSERTED', 'waste_reason', canonical.reason_id, canonical, context),
  recordWaste: (canonical, context) => administrativeEvent('WASTE_RECORDED', 'waste_movement', canonical.waste_id, { ...canonical, inventory_movement_type: canonical.inventory_movement_type || 'salida_por_merma' }, context),
  upsertInventoryAdjustmentReason: (canonical, context) => administrativeEvent('INVENTORY_ADJUSTMENT_REASON_UPSERTED', 'inventory_adjustment_reason', canonical.reason_code, canonical, context),
  recordInventoryAdjustment: (canonical, context) => administrativeEvent(
    'INVENTORY_MOVEMENT_RECORDED',
    'inventory_movement',
    canonical.adjustment_id,
    { ...canonical, movement_type: 'inventory_adjustment' },
    context
  ),
  requestApproval: (canonical, context) => administrativeEvent('APPROVAL_REQUESTED', 'approval_request', canonical.approval_request_id, canonical, context),
  approve: (canonical, context) => administrativeEvent('APPROVAL_APPROVED', 'approval_decision', `${canonical.approval_request_id}:approved`, { ...canonical, decision: 'approved' }, context),
  reject: (canonical, context) => administrativeEvent('APPROVAL_REJECTED', 'approval_decision', `${canonical.approval_request_id}:rejected`, { ...canonical, decision: 'rejected' }, context),
};

function recordWasteWithInventoryMovement(canonical, context) {
  const waste = builders.recordWaste(canonical, context);
  const movement = administrativeEvent('INVENTORY_MOVEMENT_RECORDED', 'inventory_movement', `${canonical.waste_id}:salida_por_merma`, {
    movement_type: 'salida_por_merma',
    waste_id: canonical.waste_id,
    reason_id: canonical.reason_id,
    quantity: -Math.abs(Number(canonical.quantity)),
    estimated_cost: canonical.estimated_cost,
    actor_id: canonical.operator_user_id,
    warehouse_id: canonical.warehouse_id,
    occurred_at: canonical.occurred_at,
    product_id: canonical.product_id || null,
    supply_id: canonical.supply_id || null,
  }, { ...context, correlationId: waste.correlation_id, causationId: waste.event_id });
  return [waste, movement];
}

module.exports = { REQUIRED_FIELDS_BY_EVENT, validateCanonical, administrativeEvent, recordWasteWithInventoryMovement, ...builders };
