const ProofLedger = require('../models/ProofLedger');
const chainTransactionService = require('./chainTransactionService');
const pointLedgerService = require('./pointLedgerService');
const persistentStore = require('./persistentStore');
const blockService = require('./blockService');
const { validateProofPayload } = require('../contracts/proofContract');

const state = persistentStore.readState();
const proofs = state.proofs;

async function createProof(payload = {}) {
  const normalizedPayload = validateProofPayload(payload);
  const existingProof = findDuplicateProof(normalizedPayload);

  if (existingProof) {
    return {
      proof: existingProof,
      created: false
    };
  }

  const proof = new ProofLedger({
    sourceService: normalizedPayload.sourceService,
    proofType: normalizedPayload.proofType,
    actorId: normalizedPayload.actorId,
    targetId: normalizedPayload.targetId,
    deviceId: normalizedPayload.deviceId,
    nodeId: normalizedPayload.nodeId,
    externalProofId: normalizedPayload.externalProofId,
    idempotencyKey: normalizedPayload.idempotencyKey,
    userIdHash: normalizedPayload.userIdHash,
    contentHash: normalizedPayload.contentHash,
    chunkHash: normalizedPayload.chunkHash,
    payloadHash: normalizedPayload.payloadHash,
    proofHash: normalizedPayload.proofHash,
    bundleHash: normalizedPayload.bundleHash,
    merkleRoot: normalizedPayload.merkleRoot,
    metadata: normalizedPayload.metadata
  });

  const transaction = await chainTransactionService.submitTransaction({
    sourceService: proof.sourceService,
    actionType: `PROOF_${proof.proofType}`,
    actorId: proof.actorId,
    targetId: proof.targetId,
    metadata: {
      proofId: proof.id,
      deviceId: proof.deviceId,
      nodeId: proof.nodeId,
      externalProofId: proof.externalProofId,
      idempotencyKey: proof.idempotencyKey,
      userIdHash: proof.userIdHash,
      contentHash: proof.contentHash,
      chunkHash: proof.chunkHash,
      payloadHash: proof.payloadHash,
      proofHash: proof.proofHash,
      bundleHash: proof.bundleHash,
      merkleRoot: proof.merkleRoot,
      ...proof.metadata
    }
  });

  proof.attachTransaction(transaction);
  proofs.unshift(proof);

  pointLedgerService.rewardProof(proof, {
    transactionId: transaction.id,
    txHash: transaction.txHash
  });

  const blockResult = blockService.autoMineIfReady();
  persistentStore.writeState(state);

  return {
    proof,
    block: blockResult.created ? blockResult.block : null,
    created: true
  };
}

function listProofs() {
  return proofs;
}

function getProof(proofId) {
  return proofs.find((proof) => proof.id === proofId);
}

function increment(map, key, amount = 1) {
  const safeKey = key || 'UNKNOWN';
  map[safeKey] = (map[safeKey] || 0) + amount;
}

function getProofStats() {
  const byService = {};
  const byProofType = {};
  const byStatus = {};
  const byServiceAndType = {};
  const actorSet = new Set();

  for (const proof of proofs) {
    increment(byService, proof.sourceService);
    increment(byProofType, proof.proofType);
    increment(byStatus, proof.status);

    if (proof.actorId) actorSet.add(String(proof.actorId));

    const service = proof.sourceService || 'UNKNOWN';
    const proofType = proof.proofType || 'UNKNOWN';
    if (!byServiceAndType[service]) byServiceAndType[service] = {};
    increment(byServiceAndType[service], proofType);
  }

  return {
    totalProofs: proofs.length,
    uniqueActors: actorSet.size,
    byService,
    byProofType,
    byStatus,
    byServiceAndType,
    latestCreatedAt: proofs[0]?.createdAt || null
  };
}

function findDuplicateProof(payload) {
  if (payload.idempotencyKey) {
    return proofs.find((proof) => (
      proof.sourceService === payload.sourceService &&
      proof.idempotencyKey === payload.idempotencyKey
    ));
  }

  if (payload.externalProofId) {
    return proofs.find((proof) => (
      proof.sourceService === payload.sourceService &&
      proof.externalProofId === payload.externalProofId
    ));
  }

  return null;
}

module.exports = {
  createProof,
  listProofs,
  getProof,
  getProofStats
};
