const BackupIndex = require('../models/BackupIndex');
const DistributedChunk = require('../models/DistributedChunk');
const persistentStore = require('./persistentStore');
const chainTransactionService = require('./chainTransactionService');

const state = persistentStore.readState();
const backupIndexes = state.backupIndexes;
const distributedChunks = state.distributedChunks;

function save() {
  persistentStore.writeState(state);
}

function listBackups() {
  return backupIndexes;
}

function listChunks() {
  return distributedChunks;
}

function getBackup(backupId) {
  return backupIndexes.find((backup) => backup.backupId === backupId || backup.id === backupId);
}

function getBackupChunks(backupId) {
  return distributedChunks.filter((chunk) => chunk.backupId === backupId || chunk.fileId === backupId);
}

function computeBackupStatus(backup) {
  const chunks = getBackupChunks(backup.backupId);
  const verified = chunks.filter((chunk) => chunk.status === 'verified').length;
  const stored = chunks.filter((chunk) => chunk.status === 'stored' || chunk.status === 'verified').length;
  const failed = chunks.filter((chunk) => ['missing', 'corrupt', 'failed'].includes(chunk.status)).length;
  const required = Number(backup.requiredChunks || backup.totalChunks || 0);

  if (failed > 0 && verified < required) return 'damaged';
  if (required > 0 && verified >= required) return 'recoverable';
  if (stored > 0) return 'distributed';
  return backup.status || 'ready';
}

async function createBackup(payload = {}) {
  const existing = payload.backupId ? getBackup(payload.backupId) : null;
  if (existing) {
    const updated = new BackupIndex(existing).update(payload);
    Object.assign(existing, updated);
    save();
    return { created: false, backup: existing };
  }

  const backup = new BackupIndex(payload);
  backupIndexes.unshift(backup);
  save();

  await chainTransactionService.submitTransaction({
    sourceService: payload.sourceService || 'AISFOR',
    actionType: 'BACKUP_ANCHOR',
    actorId: backup.userIdHash || backup.userId || null,
    targetId: backup.backupId,
    metadata: {
      backupId: backup.backupId,
      sourceType: backup.sourceType,
      originalSize: backup.originalSize,
      originalHash: backup.originalHash,
      encryptedHash: backup.encryptedHash,
      merkleRoot: backup.merkleRoot,
      totalChunks: backup.totalChunks,
      requiredChunks: backup.requiredChunks
    }
  });

  return { created: true, backup };
}

async function upsertChunk(payload = {}) {
  const chunkKey = payload.chunkId || `${payload.backupId || payload.fileId}:${payload.chunkIndex}:${payload.nodeId || ''}`;
  let chunk = distributedChunks.find((item) => item.chunkId === chunkKey || item.id === payload.id);
  let created = false;

  if (!chunk) {
    chunk = new DistributedChunk({ ...payload, chunkId: chunkKey });
    distributedChunks.unshift(chunk);
    created = true;
  } else {
    Object.assign(chunk, new DistributedChunk(chunk).update(payload));
  }

  const backup = payload.backupId ? getBackup(payload.backupId) : null;
  if (backup) {
    backup.status = computeBackupStatus(backup);
    backup.updatedAt = new Date().toISOString();
  }

  save();

  if (chunk.status === 'stored') {
    await chainTransactionService.submitTransaction({
      sourceService: payload.sourceService || 'AISFOR',
      actionType: 'CHUNK_STORED',
      actorId: chunk.nodeId,
      targetId: chunk.chunkId,
      metadata: {
        backupId: chunk.backupId,
        fileId: chunk.fileId,
        chunkId: chunk.chunkId,
        chunkIndex: chunk.chunkIndex,
        chunkHash: chunk.chunkHash,
        encryptedSize: chunk.encryptedSize,
        nodeId: chunk.nodeId
      }
    });
  }

  if (chunk.status === 'verified') {
    await chainTransactionService.submitTransaction({
      sourceService: payload.sourceService || 'AISFOR',
      actionType: 'CHUNK_PROOF',
      actorId: chunk.nodeId,
      targetId: chunk.chunkId,
      metadata: {
        backupId: chunk.backupId,
        fileId: chunk.fileId,
        chunkId: chunk.chunkId,
        chunkIndex: chunk.chunkIndex,
        chunkHash: chunk.chunkHash,
        nodeId: chunk.nodeId,
        lastVerifiedAt: chunk.lastVerifiedAt
      }
    });
  }

  return { created, chunk };
}

function getBackupStats() {
  const byStatus = backupIndexes.reduce((acc, backup) => {
    const status = backup.status || 'ready';
    acc[status] = (acc[status] || 0) + 1;
    return acc;
  }, {});

  const chunkByStatus = distributedChunks.reduce((acc, chunk) => {
    const status = chunk.status || 'assigned';
    acc[status] = (acc[status] || 0) + 1;
    return acc;
  }, {});

  return {
    backups: backupIndexes.length,
    chunks: distributedChunks.length,
    byStatus,
    chunkByStatus
  };
}

module.exports = {
  listBackups,
  listChunks,
  getBackup,
  getBackupChunks,
  createBackup,
  upsertChunk,
  getBackupStats
};
