const NodeParticipant = require('../models/NodeParticipant');
const NodeEconomyLedger = require('../models/NodeEconomyLedger');
const chainTransactionService = require('./chainTransactionService');
const persistentStore = require('./persistentStore');
const pointLedgerService = require('./pointLedgerService');

const state = persistentStore.readState();
const nodes = state.nodes;
const economyEntries = state.nodeEconomyEntries;

const defaultNodes = [
  { serviceName: 'AISFOR', endpoint: 'mock://aisfor', role: 'PROOF_PRODUCER', status: 'active' },
  { serviceName: 'AISTALK', endpoint: 'mock://aistalk', role: 'PROOF_PRODUCER', status: 'active' },
  { serviceName: 'AISGROUP', endpoint: 'mock://aisgroup', role: 'PROOF_PRODUCER', status: 'active' },
  { serviceName: 'AISMALL', endpoint: 'mock://aismall', role: 'PROOF_PRODUCER', status: 'active' }
];

function save() {
  state.nodes = nodes;
  state.nodeEconomyEntries = economyEntries;
  persistentStore.writeState(state);
}

function seedDefaultNodes() {
  if (nodes.length > 0) return;

  defaultNodes.forEach((payload) => {
    nodes.push(new NodeParticipant({
      ...payload,
      deviceName: `${payload.serviceName} mock participant`,
      metadata: { seeded: true }
    }));
  });
  save();
}

function listNodes() {
  seedDefaultNodes();
  return nodes;
}

function getNode(nodeId) {
  return listNodes().find((node) => node.nodeId === nodeId || node.id === nodeId);
}

function registerNode(payload = {}) {
  seedDefaultNodes();
  const existing = payload.nodeId ? getNode(payload.nodeId) : null;

  if (existing) {
    Object.assign(existing, new NodeParticipant({ ...existing, ...payload, id: existing.id, nodeId: existing.nodeId }));
    save();
    return { node: existing, created: false };
  }

  const node = new NodeParticipant({ ...payload, status: payload.status || 'pending' });
  nodes.unshift(node);
  save();
  return { node, created: true };
}

function heartbeat(nodeId, payload = {}) {
  const node = getNode(nodeId);
  if (!node) return null;

  if (typeof node.applyHeartbeat === 'function') {
    node.applyHeartbeat(payload);
  } else {
    Object.assign(node, new NodeParticipant(node));
    node.applyHeartbeat(payload);
  }

  save();
  return node;
}

function updateStatus(nodeId, status) {
  const node = getNode(nodeId);
  if (!node || !NodeParticipant.VALID_STATUSES.has(status)) return null;

  node.status = status;
  if (status === 'exited') node.exitedAt = new Date().toISOString();
  if (status === 'warning') node.warningCount = Number(node.warningCount || 0) + 1;
  save();
  return node;
}

async function recordEconomyEvent(node, {
  eventType,
  amount,
  reason,
  refType,
  refId,
  metadata
} = {}) {
  const value = Number(amount || 0);
  const transaction = await chainTransactionService.submitTransaction({
    sourceService: 'AISFOR_CHAIN',
    actionType: eventType,
    actorId: node.userId || node.userIdHash || node.nodeId,
    targetId: node.nodeId,
    amount: value,
    metadata: {
      nodeId: node.nodeId,
      serviceName: node.serviceName,
      reason: reason || '',
      refType: refType || null,
      refId: refId || null,
      ...metadata
    }
  });

  const entry = new NodeEconomyLedger({
    nodeId: node.nodeId,
    userId: node.userId || node.userIdHash || null,
    sourceService: node.serviceName || 'AISFOR_CHAIN',
    eventType,
    amount: value,
    balanceLocked: node.lockedPoint,
    balanceReward: node.rewardPoint,
    balanceSlashed: node.slashPoint,
    reason,
    refType,
    refId,
    transactionId: transaction.id,
    txHash: transaction.txHash,
    metadata
  });

  economyEntries.unshift(entry);

  if (eventType === 'NODE_REWARD' || eventType === 'NODE_SLASH') {
    pointLedgerService.createEntry({
      actorId: node.userId || node.userIdHash || node.nodeId,
      sourceService: node.serviceName || 'AISFOR_CHAIN',
      reason: eventType,
      points: eventType === 'NODE_SLASH' ? -Math.abs(value) : value,
      refType: 'node',
      refId: node.nodeId,
      metadata: {
        economyEntryId: entry.id,
        transactionId: transaction.id,
        txHash: transaction.txHash,
        reason: reason || ''
      }
    });
  }

  save();

  return {
    node,
    entry,
    transaction
  };
}

async function lockPledge(nodeId, amount, reason = 'node pledge locked') {
  const node = getNode(nodeId);
  if (!node) return null;

  const value = Math.max(0, Number(amount || 0));
  node.pledgedPoint = Number(node.pledgedPoint || 0) + value;
  node.lockedPoint = Number(node.lockedPoint || 0) + value;

  return recordEconomyEvent(node, {
    eventType: 'NODE_PLEDGE_LOCKED',
    amount: value,
    reason
  });
}

async function rewardNode(nodeId, amount, reason = 'node reward', metadata = {}) {
  const node = getNode(nodeId);
  if (!node) return null;

  const value = Math.max(0, Number(amount || 0));
  node.rewardPoint = Number(node.rewardPoint || 0) + value;

  return recordEconomyEvent(node, {
    eventType: 'NODE_REWARD',
    amount: value,
    reason,
    metadata
  });
}

async function slashNode(nodeId, amount, reason = 'node slash', metadata = {}) {
  const node = getNode(nodeId);
  if (!node) return null;

  const value = Math.max(0, Number(amount || 0));
  node.slashPoint = Number(node.slashPoint || 0) + value;
  node.lockedPoint = Math.max(0, Number(node.lockedPoint || 0) - value);
  node.slashCount = Number(node.slashCount || 0) + 1;
  node.status = 'slashed';
  node.metadata = { ...(node.metadata || {}), lastSlashReason: reason || 'unspecified' };

  return recordEconomyEvent(node, {
    eventType: 'NODE_SLASH',
    amount: value,
    reason,
    metadata
  });
}

function listEconomyEntries() {
  return economyEntries;
}

function getEconomyStats() {
  return nodes.reduce((acc, node) => {
    acc.totalPledged += Number(node.pledgedPoint || 0);
    acc.totalLocked += Number(node.lockedPoint || 0);
    acc.totalRewarded += Number(node.rewardPoint || 0);
    acc.totalSlashed += Number(node.slashPoint || 0);
    return acc;
  }, {
    totalPledged: 0,
    totalLocked: 0,
    totalRewarded: 0,
    totalSlashed: 0,
    totalEntries: economyEntries.length
  });
}

module.exports = {
  listNodes,
  getNode,
  registerNode,
  heartbeat,
  updateStatus,
  lockPledge,
  rewardNode,
  slashNode,
  listEconomyEntries,
  getEconomyStats
};
