'use strict';

const sync = require('../modules/sync');
const { HANDLERS } = require('./commandHandlers');

const CONFIG_COMMANDS = new Set([
  'UPSERT_PRODUCT',
  'UPSERT_PRODUCT_WITH_MODIFIERS',
  'CREATE_INVENTORY_WASTE',
  'UPSERT_WAREHOUSE',
  'CREATE_PURCHASE',
  'purchase.order.create',
  'CREATE_INVENTORY_ENTRY',
  'CREATE_STOCK_TRANSFER',
  'UPSERT_PAYMENT_METHOD',
  'UPSERT_WAITER',
  'UPSERT_USER',
  'config.product.create',
  'config.product.update',
  'config.product.price.update',
  'config.recipe.update',
  'config.foodcost.recalculate',
  'config.inventory.adjust',
  'config.payment_method.update',
  'billing.provider.config.update',
  'billing.micfdi.real_stamp',
  'catalog.import.preview',
  'catalog.import.apply',
  'catalog.product.upsert',
  'catalog.product.disable',
  'catalog.price.update',
  'catalog.user.upsert',
  'catalog.waiter.upsert',
  'catalog.recipe.upsert',
  'catalog.inventory.item.upsert',
  'catalog.payment_method.upsert',
  'catalog.supplier.upsert',
  'catalog.bulk_apply',
  'inventory.snapshot.request',
  'inventory.count.apply',
]);

async function executeCommand(command, config = {}, deps = {}) {
  if (!command?.command_id || !command?.command_type) throw new Error('command_id y command_type requeridos');
  const received = await sync.queue.receiveCommand(command);
  if (!received.accepted) {
    if (received.command?.status === 'failed') {
      const err = new Error(received.command.error || 'command_previously_failed');
      err.code = 'command_previously_failed';
      throw err;
    }
    if (received.command?.status === 'applied') return { duplicate: true, command: received.command };
  }

  await sync.queue.markCommandApplying(command);
  const handler = getHandler(command.command_type, deps);
  try {
    const result = await handler(command, config, deps);
    await recordLocalAudit(command, 'applied', result, config, deps);
    await recordConfirmationEvent(command, result, config, deps);
    await sync.queue.markCommandApplied(command, result);
    return { duplicate: false, result };
  } catch (err) {
    await recordLocalAudit(command, 'failed', { error: err.message, code: err.code || 'command_error' }, config, deps).catch(() => {});
    await sync.queue.markCommandError(command, err.message);
    throw err;
  }
}

function getHandler(commandType, deps = {}) {
  if (deps.handlers?.[commandType]) return deps.handlers[commandType];
  if (commandType === 'sync.force_push' || commandType === 'sync.force_snapshot') {
    return async (command, config) => {
      const flush = deps.flushOnce || sync.service.flushOnce;
      return flush({ limit: config.syncBatchSize || 100, reason: command.command_type });
    };
  }
  if (HANDLERS[commandType]) return HANDLERS[commandType];
  if (CONFIG_COMMANDS.has(commandType)) {
    return async () => {
      const err = new Error(`handler_not_implemented: ${commandType}`);
      err.code = 'handler_not_implemented';
      throw err;
    };
  }
  return async () => {
    throw new Error(`command_type no soportado por conector: ${commandType}`);
  };
}

async function recordLocalAudit(command, status, result = {}, config = {}, deps = {}) {
  const enqueue = deps.enqueueEvent || sync.service.enqueueEvent;
  return enqueue({
    entity_type: 'cloud.command.audit',
    entity_id: command.command_id,
    operation: status === 'applied' ? 'ack' : 'update',
    idempotency_key: `${command.idempotency_key || command.command_id}:audit:${status}`,
    payload: {
      command_id: command.command_id,
      command_type: command.command_type,
      status,
      result,
      cloud_created_at: command.created_at || null,
    },
  }, config.syncContext);
}

async function recordConfirmationEvent(command, result = {}, config = {}, deps = {}) {
  const enqueue = deps.enqueueEvent || sync.service.enqueueEvent;
  return enqueue({
    entity_type: 'cloud.command.applied',
    entity_id: command.command_id,
    operation: 'ack',
    idempotency_key: `${command.idempotency_key || command.command_id}:confirmation`,
    payload: {
      command_id: command.command_id,
      command_type: command.command_type,
      result,
    },
  }, config.syncContext);
}

module.exports = { executeCommand, getHandler, CONFIG_COMMANDS, recordLocalAudit, recordConfirmationEvent };
