const RewardLedger = require('../models/RewardLedger');
const persistentStore = require('./persistentStore');
const tokenomicsService = require('./tokenomicsService');

function getState() {
  return persistentStore.readState();
}

function scorePayload(payload = {}) {
  const status = tokenomicsService.getStatus();
  const weights = status.weights;
  const scores = {
    service: Number(payload.serviceScore || 0) * weights.service,
    content: Number(payload.contentScore || 0) * weights.content,
    storage: Number(payload.storageScore || 0) * weights.storage,
    uptime: Number(payload.uptimeScore || 0) * weights.uptime,
    verification: Number(payload.verificationScore || 0) * weights.verification,
    commerce: Number(payload.commerceScore || 0) * weights.commerce,
    emoticon: Number(payload.emoticonScore || 0) * weights.emoticon,
    ecosystem: Number(payload.ecosystemScore || 0) * weights.ecosystem
  };
  const rewardScore = Object.values(scores).reduce((sum, value) => sum + (Number.isFinite(value) ? value : 0), 0);
  const flags = [];

  if (rewardScore > status.limits.suspiciousScoreThreshold) flags.push('SUSPICIOUS_SCORE');
  if (payload.refund || payload.cancelled) flags.push('EXCLUDED_REFUND_OR_CANCELLED');
  if (payload.selfTrade) flags.push('SELF_TRADE_REVIEW');
  if (Array.isArray(payload.sourcePolicyFlags)) flags.push(...payload.sourcePolicyFlags);
  if (Array.isArray(payload.sourcePolicyWarnings)) flags.push(...payload.sourcePolicyWarnings);

  return {
    scores,
    rewardScore,
    rewardAmount: flags.includes('EXCLUDED_REFUND_OR_CANCELLED') ? 0 : rewardScore,
    flags
  };
}

function getDayKey(value) {
  const date = value ? new Date(value) : new Date();
  if (Number.isNaN(date.getTime())) return new Date().toISOString().slice(0, 10);
  return date.toISOString().slice(0, 10);
}

function getEntryDay(entry = {}) {
  return getDayKey(entry.metadata?.sourceCreatedAt || entry.createdAt);
}

function calculateCapReview({ entries = [], payload = {}, scored = {}, status }) {
  const actorId = payload.actorId || payload.userId || payload.nodeId || 'AISFOR_UNKNOWN';
  const category = payload.category || 'ecosystem';
  const dayKey = getDayKey(payload.sourceCreatedAt || payload.createdAt);
  const actorDailyTotal = entries
    .filter((entry) => (entry.actorId || 'AISFOR_UNKNOWN') === actorId && getEntryDay(entry) === dayKey)
    .reduce((sum, entry) => sum + Number(entry.rewardAmount || 0), 0);
  const categoryDailyTotal = entries
    .filter((entry) => (entry.category || 'ecosystem') === category && getEntryDay(entry) === dayKey)
    .reduce((sum, entry) => sum + Number(entry.rewardAmount || 0), 0);
  const dailyUserCap = Number(status.limits.dailyUserCap || 0);
  const dailyCategoryCap = Number(status.limits.dailyCategoryCap || 0);
  const requestedAmount = Number(scored.rewardAmount || 0);
  if (requestedAmount < 0) {
    return {
      dayKey,
      actorId,
      category,
      requestedAmount,
      cappedAmount: requestedAmount,
      actorDailyTotal,
      categoryDailyTotal,
      dailyUserCap,
      dailyCategoryCap,
      userRemainingBefore: null,
      categoryRemainingBefore: null,
      flags: []
    };
  }
  const userRemaining = Math.max(0, dailyUserCap - actorDailyTotal);
  const categoryRemaining = Math.max(0, dailyCategoryCap - categoryDailyTotal);
  const cappedAmount = Math.max(0, Math.min(requestedAmount, userRemaining, categoryRemaining));
  const flags = [];

  if (requestedAmount > userRemaining) flags.push('DAILY_USER_CAP_APPLIED');
  if (requestedAmount > categoryRemaining) flags.push('DAILY_CATEGORY_CAP_APPLIED');

  return {
    dayKey,
    actorId,
    category,
    requestedAmount,
    cappedAmount,
    actorDailyTotal,
    categoryDailyTotal,
    dailyUserCap,
    dailyCategoryCap,
    userRemainingBefore: userRemaining,
    categoryRemainingBefore: categoryRemaining,
    flags
  };
}

function createSimulationEntry(payload = {}) {
  const status = tokenomicsService.getStatus();
  const state = getState();
  const entries = Array.isArray(state.rewardEntries) ? state.rewardEntries : [];
  const idempotencyKey = payload.idempotencyKey || payload.externalEventKey || '';
  if (idempotencyKey) {
    const existing = entries.find((entry) => entry.metadata && entry.metadata.idempotencyKey === idempotencyKey);
    if (existing) return existing;
  }
  const scored = scorePayload(payload);
  const capReview = calculateCapReview({ entries, payload, scored, status });
  scored.flags.push(...capReview.flags);
  const entry = new RewardLedger({
    actorId: payload.actorId,
    sourceService: payload.sourceService,
    category: payload.category || 'ecosystem',
    actionType: payload.actionType || 'SIMULATION',
    rewardScore: scored.rewardScore,
    rewardAmount: capReview.cappedAmount,
    symbol: status.config.symbol,
    mode: status.config.rewardMode,
    refType: payload.refType,
    refId: payload.refId,
    metadata: {
      scores: scored.scores,
      note: payload.note || '',
      tokenomicsPhase: status.config.phase,
      idempotencyKey,
      externalEventKey: payload.externalEventKey || '',
      externalProofHash: payload.externalProofHash || '',
      sourceCreatedAt: payload.sourceCreatedAt || null,
      pointAmount: Number(payload.pointAmount || 0),
      sourcePolicyValidation: payload.sourcePolicyValidation || null,
      capReview
    },
    flags: scored.flags
  });

  entries.unshift(entry);
  state.rewardEntries = entries;
  persistentStore.writeState(state);
  return entry;
}

function createExternalPointReward(payload = {}) {
  const category = String(payload.category || payload.pointCategory || 'ecosystem').trim();
  const pointAmount = Number(payload.pointAmount || payload.rewardAmount || 0);
  const score = Number.isFinite(pointAmount) ? pointAmount : 0;
  const normalizedCategory = [
    'service',
    'content',
    'storage',
    'uptime',
    'verification',
    'commerce',
    'emoticon',
    'ecosystem'
  ].includes(category) ? category : (
    category === 'activity' ? 'service' : 'ecosystem'
  );
  const rewardPayload = {
    actorId: payload.actorId || payload.userId || payload.nodeId || 'AISFOR_UNKNOWN',
    sourceService: payload.sourceService || 'AISFOR',
    category: normalizedCategory,
    actionType: payload.actionType || payload.eventType || 'POINT_LEDGER_EVENT',
    refType: payload.refType || 'AISFOR_POINT_LEDGER',
    refId: payload.refId || payload.eventKey || payload.pointProofHash,
    idempotencyKey: payload.idempotencyKey || `${payload.sourceService || 'AISFOR'}:POINT:${payload.eventKey || payload.pointProofHash || payload.refId || ''}`,
    externalEventKey: payload.eventKey || '',
    externalProofHash: payload.pointProofHash || payload.externalProofHash || '',
    sourceCreatedAt: payload.createdAt || payload.sourceCreatedAt || null,
    pointAmount,
    note: payload.note || payload.reason || 'AISFOR point ledger mirrored reward',
    refund: Boolean(payload.refund),
    cancelled: Boolean(payload.cancelled),
    selfTrade: Boolean(payload.selfTrade),
    sourcePolicyFlags: payload.sourcePolicyFlags || [],
    sourcePolicyWarnings: payload.sourcePolicyWarnings || [],
    sourcePolicyValidation: payload.sourcePolicyValidation || null
  };
  rewardPayload[`${normalizedCategory}Score`] = score;
  return createSimulationEntry(rewardPayload);
}

function dryRunExternalPointReward(payload = {}) {
  const category = String(payload.category || payload.pointCategory || 'ecosystem').trim();
  const pointAmount = Number(payload.pointAmount || payload.rewardAmount || 0);
  const score = Number.isFinite(pointAmount) ? pointAmount : 0;
  const normalizedCategory = [
    'service',
    'content',
    'storage',
    'uptime',
    'verification',
    'commerce',
    'emoticon',
    'ecosystem'
  ].includes(category) ? category : (
    category === 'activity' ? 'service' : 'ecosystem'
  );
  const rewardPayload = {
    actorId: payload.actorId || payload.userId || payload.nodeId || 'AISFOR_UNKNOWN',
    sourceService: payload.sourceService || 'AISFOR',
    category: normalizedCategory,
    actionType: payload.actionType || payload.eventType || 'POINT_LEDGER_EVENT',
    refType: payload.refType || 'AISFOR_POINT_LEDGER',
    refId: payload.refId || payload.eventKey || payload.pointProofHash,
    idempotencyKey: payload.idempotencyKey || `${payload.sourceService || 'AISFOR'}:POINT:${payload.eventKey || payload.pointProofHash || payload.refId || ''}`,
    externalEventKey: payload.eventKey || '',
    externalProofHash: payload.pointProofHash || payload.externalProofHash || '',
    sourceCreatedAt: payload.createdAt || payload.sourceCreatedAt || null,
    pointAmount,
    note: payload.note || payload.reason || 'AISFOR point ledger mirrored reward',
    refund: Boolean(payload.refund),
    cancelled: Boolean(payload.cancelled),
    selfTrade: Boolean(payload.selfTrade),
    sourcePolicyFlags: payload.sourcePolicyFlags || [],
    sourcePolicyWarnings: payload.sourcePolicyWarnings || [],
    sourcePolicyValidation: payload.sourcePolicyValidation || null
  };
  rewardPayload[`${normalizedCategory}Score`] = score;

  const status = tokenomicsService.getStatus();
  const state = getState();
  const entries = Array.isArray(state.rewardEntries) ? state.rewardEntries : [];
  const idempotencyKey = rewardPayload.idempotencyKey || rewardPayload.externalEventKey || '';
  const existing = idempotencyKey
    ? entries.find((entry) => entry.metadata && entry.metadata.idempotencyKey === idempotencyKey)
    : null;
  const scored = scorePayload(rewardPayload);
  const capReview = calculateCapReview({ entries, payload: rewardPayload, scored, status });
  const flags = [...scored.flags, ...capReview.flags];

  return {
    ok: !existing,
    wouldWrite: !existing,
    wouldNeedReview: flags.length > 0,
    duplicate: Boolean(existing),
    existingRewardId: existing?.id || null,
    sourceService: rewardPayload.sourceService,
    actorId: rewardPayload.actorId,
    category: rewardPayload.category,
    actionType: rewardPayload.actionType,
    rewardScore: scored.rewardScore,
    requestedRewardAmount: scored.rewardAmount,
    rewardAmount: capReview.cappedAmount,
    symbol: status.config.symbol,
    mode: status.config.rewardMode,
    idempotencyKey,
    refType: rewardPayload.refType,
    refId: rewardPayload.refId,
    scores: scored.scores,
    capReview,
    flags
  };
}

function listEntries() {
  return Array.isArray(getState().rewardEntries) ? getState().rewardEntries : [];
}

function getReviewDecision(entry = {}) {
  const decision = String(entry.metadata?.reviewDecision?.decision || '').trim().toUpperCase();
  return decision || '';
}

function isReviewResolved(entry = {}) {
  return ['APPROVE', 'REJECT'].includes(getReviewDecision(entry));
}

function needsReview(entry = {}) {
  if (isReviewResolved(entry)) return false;

  const flags = Array.isArray(entry.flags) ? entry.flags : [];
  const validation = entry.metadata?.sourcePolicyValidation || null;
  const capReview = entry.metadata?.capReview || null;
  return (
    flags.length > 0 ||
    (validation && validation.ok === false) ||
    (capReview && Number(capReview.requestedAmount || 0) !== Number(capReview.cappedAmount || 0))
  );
}

function getReviewQueue(limit = 25) {
  return listEntries()
    .filter(needsReview)
    .slice(0, limit)
    .map((entry) => ({
      id: entry.id,
      actorId: entry.actorId,
      sourceService: entry.sourceService,
      category: entry.category,
      actionType: entry.actionType,
      rewardAmount: entry.rewardAmount,
      rewardScore: entry.rewardScore,
      symbol: entry.symbol,
      flags: entry.flags || [],
      validation: entry.metadata?.sourcePolicyValidation || null,
      capReview: entry.metadata?.capReview || null,
      reviewDecision: entry.metadata?.reviewDecision || null,
      createdAt: entry.createdAt,
      blockHeight: entry.blockHeight || null
    }));
}

function getStats() {
  const status = tokenomicsService.getStatus();
  const entries = listEntries();
  const reviewQueue = entries.filter(needsReview);
  const totalsByCategory = {};
  const totalsByActor = {};
  for (const category of status.categories) totalsByCategory[category] = 0;
  for (const entry of entries) {
    const category = entry.category || 'ecosystem';
    totalsByCategory[category] = (totalsByCategory[category] || 0) + Number(entry.rewardAmount || 0);
    const actor = entry.actorId || 'UNKNOWN';
    totalsByActor[actor] = (totalsByActor[actor] || 0) + Number(entry.rewardAmount || 0);
  }

  return {
    tokenomics: status,
    entryCount: entries.length,
    totalSimulatedReward: entries.reduce((sum, entry) => sum + Number(entry.rewardAmount || 0), 0),
    flaggedCount: entries.filter((entry) => Array.isArray(entry.flags) && entry.flags.length > 0).length,
    reviewCount: reviewQueue.length,
    resolvedReviewCount: entries.filter(isReviewResolved).length,
    capAppliedCount: entries.filter((entry) => {
      const capReview = entry.metadata?.capReview;
      return capReview && Number(capReview.requestedAmount || 0) !== Number(capReview.cappedAmount || 0);
    }).length,
    totalsByCategory,
    totalsByActor: Object.entries(totalsByActor)
      .map(([actorId, rewardAmount]) => ({ actorId, rewardAmount }))
      .sort((a, b) => b.rewardAmount - a.rewardAmount)
      .slice(0, 10),
    latestEntries: entries.slice(0, 8)
  };
}

module.exports = {
  calculateCapReview,
  createExternalPointReward,
  dryRunExternalPointReward,
  createSimulationEntry,
  getReviewQueue,
  getStats,
  getReviewDecision,
  isReviewResolved,
  listEntries,
  needsReview,
  scorePayload
};
