// save-validation.test.ts — Tests anti-cheat validation logic
// Ported from backend saveControllers.validateGameState for unit testing

import { describe, it, expect } from "vitest";
import { DEFAULT_STATE } from "../core/economy";

// Reproduce the validation logic client-side for testing
const MAX_PRODUCTION_PER_SECOND = 750_000;
const CHEAT_MARGIN = 1.1;

interface PreviousSave {
  last_save: string;
  game_state: { resources: number };
}

function validateGameState(
  gameState: { resources: number; generators?: unknown[] },
  previousSave: PreviousSave | null
): { valid: boolean; reason?: string } {
  if (!gameState || typeof gameState !== "object") {
    return { valid: false, reason: "Invalid game state format" };
  }

  if (typeof gameState.resources !== "number" || gameState.resources < 0) {
    return { valid: false, reason: "Invalid resources value" };
  }

  if (!Array.isArray(gameState.generators)) {
    return { valid: false, reason: "Invalid generators" };
  }

  if (!previousSave) {
    return { valid: true };
  }

  const lastSaveTime = new Date(previousSave.last_save).getTime();
  const now = Date.now();
  const elapsedSeconds = Math.max((now - lastSaveTime) / 1000, 0);

  const maxPossibleResources =
    MAX_PRODUCTION_PER_SECOND * elapsedSeconds * CHEAT_MARGIN;

  const previousResources = previousSave.game_state?.resources ?? 0;
  const resourceDelta = gameState.resources - previousResources;

  if (resourceDelta > maxPossibleResources && resourceDelta > 0) {
    return {
      valid: false,
      reason: `Resource gain exceeds maximum possible`,
    };
  }

  return { valid: true };
}

describe("Anti-cheat validation", () => {
  it("accepts first save (no previous)", () => {
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: 1000 },
      null
    );
    expect(result.valid).toBe(true);
  });

  it("accepts legitimate resource gain", () => {
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: 100_000 },
      { last_save: fiveMinutesAgo, game_state: { resources: 50_000 } }
    );
    expect(result.valid).toBe(true);
  });

  it("rejects impossibly large resource gain", () => {
    const oneSecondAgo = new Date(Date.now() - 1000).toISOString();
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: 999_999_999_999 },
      { last_save: oneSecondAgo, game_state: { resources: 0 } }
    );
    expect(result.valid).toBe(false);
    expect(result.reason).toContain("exceeds maximum");
  });

  it("rejects negative resources", () => {
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: -100 },
      null
    );
    expect(result.valid).toBe(false);
  });

  it("rejects invalid game state format", () => {
    const result = validateGameState(null as any, null);
    expect(result.valid).toBe(false);
  });

  it("rejects missing generators array", () => {
    const result = validateGameState(
      { resources: 100, generators: "not an array" } as any,
      null
    );
    expect(result.valid).toBe(false);
  });

  it("accepts resource loss (spending on generators)", () => {
    const oneMinuteAgo = new Date(Date.now() - 60 * 1000).toISOString();
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: 1000 },
      { last_save: oneMinuteAgo, game_state: { resources: 5000 } }
    );
    expect(result.valid).toBe(true); // delta < 0, so always OK
  });

  it("accepts gains within max production window", () => {
    const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
    // 10 min × 750,000/s × 1.1 = 495,000,000
    const result = validateGameState(
      { ...DEFAULT_STATE, resources: 400_000_000 },
      { last_save: tenMinutesAgo, game_state: { resources: 0 } }
    );
    expect(result.valid).toBe(true);
  });
});
