'use strict';

const storage = require('../storage');
const deadLetters = require('../dead-letter');
const { assertSafeEvent } = require('../../../agent/softrestaurant-bridge/security/payload-security');

function now() {
  return new Date().toISOString();
}

function acknowledgementResult(acknowledgement) {
  return acknowledgement.result || (acknowledgement.status === 'failed' ? 'failed_validation' : 'ok');
}

function queueAcknowledgement(item, status, result, details = {}, error = null) {
  item.ack_queue = item.ack_queue || [];
  const pending = item.ack_queue.find((acknowledgement) => acknowledgement.status === status && acknowledgementResult(acknowledgement) === result && !acknowledgement.sent_at);
  if (pending) {
    pending.result = result;
    pending.details = details;
    pending.error = error;
    return;
  }
  item.ack_queue.push({ status, result, details, error, queued_at: now(), sent_at: null });
}

function enqueue(events) {
  for (const event of events) assertSafeEvent(event);
  storage.update('inbox', [], (items) => {
    for (const event of events) {
      const existing = items.find((item) => item.event.event_id === event.event_id);
      if (existing) {
        queueAcknowledgement(existing, 'received', 'duplicate');
        continue;
      }
      const item = { event, status: 'stored_in_inbox', retries: 0, next_attempt_at: now(), lease_until: null, created_at: now(), ack_queue: [] };
      queueAcknowledgement(item, 'received', 'ok');
      queueAcknowledgement(item, 'stored_in_inbox', 'ok');
      items.push(item);
    }
    return items;
  });
}

function lease(limit = 100, leaseMs = 60000) {
  const leased = [];
  const current = Date.now();
  storage.update('inbox', [], (items) => {
    for (const item of items) {
      if (leased.length >= limit) break;
      const due = ['stored_in_inbox', 'retry'].includes(item.status) && new Date(item.next_attempt_at || 0).getTime() <= current;
      const expired = item.status === 'leased' && new Date(item.lease_until || 0).getTime() <= current;
      if (!due && !expired) continue;
      item.status = 'leased';
      item.lease_until = new Date(current + leaseMs).toISOString();
      leased.push(item.event);
    }
    return items;
  });
  return leased;
}

function markApplied(eventId, details = {}) {
  storage.update('inbox', [], (items) => {
    const item = items.find((entry) => entry.event.event_id === eventId);
    if (item) {
      Object.assign(item, { status: 'applied_to_local_mirror', applied_at: now(), apply_details: details, lease_until: null });
      queueAcknowledgement(item, 'applied_to_local_mirror', details.result || 'ok', details);
    }
    return items;
  });
}

function pendingAcknowledgements(limit = 100) {
  return storage.read('inbox', [])
    .map((item) => {
      const acknowledgement = (item.ack_queue || []).find((candidate) => !candidate.sent_at);
      return acknowledgement ? {
        event_id: item.event.event_id,
        status: acknowledgement.status,
        result: acknowledgementResult(acknowledgement),
        details: acknowledgement.details || {},
        ...(acknowledgement.error ? { error: acknowledgement.error } : {}),
      } : null;
    })
    .filter(Boolean)
    .slice(0, limit);
}

function markAcknowledged(acknowledgements) {
  const wanted = new Map(acknowledgements.map((acknowledgement) => [`${acknowledgement.event_id}:${acknowledgement.status}:${acknowledgement.result}`, acknowledgement]));
  storage.update('inbox', [], (items) => {
    for (const item of items) {
      for (const acknowledgement of item.ack_queue || []) {
        if (wanted.has(`${item.event.event_id}:${acknowledgement.status}:${acknowledgementResult(acknowledgement)}`) && !acknowledgement.sent_at) acknowledgement.sent_at = now();
      }
    }
    return items;
  });
}

function markFailed(eventId, error, options = {}) {
  const maxRetries = options.maxRetries || 10;
  const baseDelayMs = options.baseDelayMs || 10000;
  storage.update('inbox', [], (items) => {
    const item = items.find((entry) => entry.event.event_id === eventId);
    if (!item) return items;
    item.retries += 1;
    item.last_error = String(error || 'unknown').slice(0, 1000);
    item.lease_until = null;
    const result = options.result === 'write_disabled' ? 'write_disabled' : 'failed_validation';
    queueAcknowledgement(item, 'failed', result, { retries: item.retries }, item.last_error);
    if (item.retries >= maxRetries) {
      item.status = 'dead_letter';
      deadLetters.append({
        stage: 'inbox_apply', error_code: 'MAX_RETRIES_EXCEEDED', event_id: eventId,
        entity_type: item.event.entity_type, entity_id: item.event.entity_id, payload_hash: item.event.payload_hash,
        safe_summary: { reason: result }, retry_count: item.retries,
      });
    } else {
      item.status = 'retry';
      item.next_attempt_at = new Date(Date.now() + Math.min(baseDelayMs * (2 ** (item.retries - 1)), 3600000)).toISOString();
    }
    return items;
  });
}

function stats() {
  return storage.read('inbox', []).reduce((result, item) => {
    result[item.status] = (result[item.status] || 0) + 1;
    return result;
  }, {});
}

module.exports = { enqueue, lease, markApplied, pendingAcknowledgements, markAcknowledged, markFailed, stats };
