import { describe, it, expect } from "vitest";
import {
  generatorCost,
  totalProductionPerSecond,
  computeIdleGains,
  applyClick,
  buyGenerator,
  applyPrestige,
  canPrestige,
  computePrestigeDna,
  canBuyEvolutionNode,
  buyEvolutionNode,
  getClickMultiplierFromTree,
  getProductionMultiplierFromTree,
  getStartBonusFromTree,
  DEFAULT_STATE,
  DEFAULT_GENERATORS,
  DEFAULT_EVOLUTION_TREE,
} from "../core/economy";

// --- PrestigePanel visibility ---

describe("PrestigePanel visibility (canPrestige guard)", () => {
  it("canPrestige = false pour resources = 0 → panneau non visible", () => {
    expect(canPrestige({ ...DEFAULT_STATE, resources: 0 })).toBe(false);
  });

  it("canPrestige = false pour resources = 999 999 → panneau non visible", () => {
    expect(canPrestige({ ...DEFAULT_STATE, resources: 999_999 })).toBe(false);
  });

  it("canPrestige = true pour resources = 1 000 000 → panneau visible", () => {
    expect(canPrestige({ ...DEFAULT_STATE, resources: 1_000_000 })).toBe(true);
  });
});

// --- Prestige reset ---

describe("applyPrestige — post-prestige state", () => {
  const prestigeState = {
    ...DEFAULT_STATE,
    resources: 1_500_000,
    generators: DEFAULT_STATE.generators.map((g) => ({ ...g, owned: 3 })),
    prestigeCount: 0,
    prestigeMultiplier: 1,
    lifetimeTadpoles: 2_000_000_000,
  };

  it("ressources = startBonus (0 sans nœud Mémoire Génétique) après prestige", () => {
    expect(applyPrestige(prestigeState).resources).toBe(0);
  });

  it("multiplicateur = 1.1 après premier prestige", () => {
    expect(applyPrestige(prestigeState).prestigeMultiplier).toBeCloseTo(1.1);
  });

  it("tous les générateurs owned = 0 après prestige", () => {
    const result = applyPrestige(prestigeState);
    expect(result.generators.every((g) => g.owned === 0)).toBe(true);
  });

  it("prestigeCount incrémenté à 1 après premier prestige", () => {
    expect(applyPrestige(prestigeState).prestigeCount).toBe(1);
  });

  it("gagne de l'ADN Ancestral au prestige", () => {
    const result = applyPrestige(prestigeState);
    const expectedDna = computePrestigeDna(2_000_000_000);
    expect(result.ancestralDna).toBe(expectedDna);
    expect(expectedDna).toBeGreaterThan(0);
  });

  it("lifetimeTadpoles reset à 0 après prestige", () => {
    expect(applyPrestige(prestigeState).lifetimeTadpoles).toBe(0);
  });

  it("arbre d'évolution persiste après prestige", () => {
    const stateWithNode = {
      ...prestigeState,
      evolutionTree: prestigeState.evolutionTree.map((n) =>
        n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
      ),
    };
    const result = applyPrestige(stateWithNode);
    expect(result.evolutionTree.find((n) => n.id === "ponte_amelioree")!.unlocked).toBe(true);
  });

  it("startBonus appliqué si Mémoire Génétique débloquée", () => {
    const stateWithMemory = {
      ...prestigeState,
      evolutionTree: prestigeState.evolutionTree.map((n) =>
        n.id === "memoire_genetique" ? { ...n, unlocked: true } : n
      ),
    };
    const result = applyPrestige(stateWithMemory);
    expect(result.resources).toBe(100);
  });
});

// --- Generator cost ---

describe("generatorCost", () => {
  it("retourne baseCost quand owned = 0", () => {
    const gen = { ...DEFAULT_GENERATORS[0], owned: 0 };
    expect(generatorCost(gen)).toBe(gen.baseCost);
  });

  it("applique la formule base × 1.15^n", () => {
    const gen = { ...DEFAULT_GENERATORS[0], owned: 2 };
    expect(generatorCost(gen)).toBe(Math.floor(gen.baseCost * Math.pow(1.15, 2)));
  });
});

// --- Production ---

describe("totalProductionPerSecond", () => {
  it("retourne 0 si aucun générateur acheté", () => {
    expect(totalProductionPerSecond(DEFAULT_STATE)).toBe(0);
  });

  it("somme correctement la production de plusieurs générateurs", () => {
    const state = {
      ...DEFAULT_STATE,
      generators: DEFAULT_STATE.generators.map((g, i) =>
        i === 0 ? { ...g, owned: 2 } : i === 1 ? { ...g, owned: 1 } : g
      ),
    };
    const expected = (DEFAULT_GENERATORS[0].baseProduction * 2 + DEFAULT_GENERATORS[1].baseProduction * 1) * 1;
    expect(totalProductionPerSecond(state)).toBeCloseTo(expected);
  });

  it("applique le multiplicateur de prestige", () => {
    const state = { ...DEFAULT_STATE, generators: DEFAULT_STATE.generators.map((g, i) => i === 0 ? { ...g, owned: 1 } : g), prestigeMultiplier: 1.5 };
    expect(totalProductionPerSecond(state)).toBeCloseTo(DEFAULT_GENERATORS[0].baseProduction * 1.5);
  });

  it("applique le multiplicateur arbre d'évolution", () => {
    const state = {
      ...DEFAULT_STATE,
      generators: DEFAULT_STATE.generators.map((g, i) => i === 0 ? { ...g, owned: 1 } : g),
      evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
        n.id === "instinct_gregaire" ? { ...n, unlocked: true } : n
      ),
    };
    expect(totalProductionPerSecond(state)).toBeCloseTo(DEFAULT_GENERATORS[0].baseProduction * 1.5);
  });
});

// --- Idle gains ---

describe("computeIdleGains (lazy calculation)", () => {
  it("calcule les gains proportionnellement au temps écoulé", () => {
    const state = {
      ...DEFAULT_STATE,
      generators: DEFAULT_STATE.generators.map((g, i) => i === 0 ? { ...g, owned: 10 } : g),
      lastTick: 0,
    };
    const gains = computeIdleGains(state, 5000); // 5 secondes
    const expected = DEFAULT_GENERATORS[0].baseProduction * 10 * 5;
    expect(gains).toBeCloseTo(expected);
  });

  it("retourne 0 si aucun temps écoulé", () => {
    const now = Date.now();
    const state = { ...DEFAULT_STATE, lastTick: now };
    expect(computeIdleGains(state, now)).toBeCloseTo(0);
  });
});

// --- Click ---

describe("applyClick", () => {
  it("augmente les ressources du clickMultiplier × prestigeMultiplier", () => {
    const state = { ...DEFAULT_STATE, clickMultiplier: 3, prestigeMultiplier: 2 };
    const result = applyClick(state);
    expect(result.resources).toBe(6);
  });

  it("applique le multiplicateur click de l'arbre", () => {
    const state = {
      ...DEFAULT_STATE,
      clickMultiplier: 1,
      prestigeMultiplier: 1,
      evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
        n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
      ),
    };
    const result = applyClick(state);
    expect(result.resources).toBe(2); // ×2 depuis Ponte Améliorée
  });

  it("incrémente lifetimeTadpoles", () => {
    const state = { ...DEFAULT_STATE, clickMultiplier: 5, prestigeMultiplier: 1 };
    const result = applyClick(state);
    expect(result.lifetimeTadpoles).toBe(5);
  });
});

// --- Buy generator ---

describe("buyGenerator", () => {
  it("retourne null si fonds insuffisants", () => {
    const result = buyGenerator(DEFAULT_STATE, "nid");
    expect(result).toBeNull();
  });

  it("achète correctement et déduit le coût", () => {
    const state = { ...DEFAULT_STATE, resources: 100 };
    const result = buyGenerator(state, "nid");
    expect(result).not.toBeNull();
    expect(result!.generators.find((g) => g.id === "nid")!.owned).toBe(1);
    expect(result!.resources).toBe(100 - DEFAULT_GENERATORS[0].baseCost);
  });
});

// --- Prestige legacy ---

describe("prestige", () => {
  it("canPrestige retourne false si < 1 000 000 ressources", () => {
    expect(canPrestige({ ...DEFAULT_STATE, resources: 999_999 })).toBe(false);
  });

  it("canPrestige retourne true si ≥ 1 000 000 ressources", () => {
    expect(canPrestige({ ...DEFAULT_STATE, resources: 1_000_000 })).toBe(true);
  });

  it("reset les ressources + générateurs + incrémente le multiplicateur", () => {
    const state = {
      ...DEFAULT_STATE,
      resources: 2_000_000,
      generators: DEFAULT_STATE.generators.map((g) => ({ ...g, owned: 5 })),
      prestigeCount: 0,
    };
    const result = applyPrestige(state);
    expect(result.resources).toBe(0);
    expect(result.prestigeCount).toBe(1);
    expect(result.prestigeMultiplier).toBeCloseTo(1.1);
    expect(result.generators.every((g) => g.owned === 0)).toBe(true);
  });
});

// --- ADN Ancestral ---

describe("computePrestigeDna", () => {
  it("retourne 0 pour 0 têtards", () => {
    expect(computePrestigeDna(0)).toBe(0);
  });

  it("retourne 150 pour 1e9 têtards (sqrt(1) = 1)", () => {
    expect(computePrestigeDna(1e9)).toBe(150);
  });

  it("retourne 212 pour 2e9 têtards (sqrt(2) ≈ 1.414)", () => {
    expect(computePrestigeDna(2e9)).toBe(Math.floor(150 * Math.sqrt(2)));
  });

  it("scaling sub-linéaire — 10× têtards ≠ 10× ADN", () => {
    const dna1 = computePrestigeDna(1e9);
    const dna10 = computePrestigeDna(10e9);
    expect(dna10 / dna1).toBeCloseTo(Math.sqrt(10), 1);
  });
});

// --- Arbre d'Évolution ---

describe("Evolution Tree", () => {
  describe("canBuyEvolutionNode", () => {
    it("peut acheter le premier nœud (pas de prérequis) avec assez d'ADN", () => {
      const state = { ...DEFAULT_STATE, ancestralDna: 5 };
      expect(canBuyEvolutionNode(state, "ponte_amelioree")).toBe(true);
    });

    it("ne peut pas acheter sans assez d'ADN", () => {
      const state = { ...DEFAULT_STATE, ancestralDna: 0 };
      expect(canBuyEvolutionNode(state, "ponte_amelioree")).toBe(false);
    });

    it("ne peut pas acheter un nœud déjà débloqué", () => {
      const state = {
        ...DEFAULT_STATE,
        ancestralDna: 100,
        evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
          n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
        ),
      };
      expect(canBuyEvolutionNode(state, "ponte_amelioree")).toBe(false);
    });

    it("ne peut pas acheter un nœud dont le prérequis n'est pas débloqué", () => {
      const state = { ...DEFAULT_STATE, ancestralDna: 100 };
      expect(canBuyEvolutionNode(state, "instinct_gregaire")).toBe(false);
    });

    it("peut acheter un nœud si le prérequis est débloqué", () => {
      const state = {
        ...DEFAULT_STATE,
        ancestralDna: 100,
        evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
          n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
        ),
      };
      expect(canBuyEvolutionNode(state, "instinct_gregaire")).toBe(true);
    });
  });

  describe("buyEvolutionNode", () => {
    it("débloque le nœud et déduit l'ADN", () => {
      const state = { ...DEFAULT_STATE, ancestralDna: 5 };
      const result = buyEvolutionNode(state, "ponte_amelioree");
      expect(result).not.toBeNull();
      expect(result!.ancestralDna).toBe(4);
      expect(result!.evolutionTree.find((n) => n.id === "ponte_amelioree")!.unlocked).toBe(true);
    });

    it("retourne null si impossible", () => {
      const result = buyEvolutionNode(DEFAULT_STATE, "ponte_amelioree");
      expect(result).toBeNull();
    });

    it("ne modifie pas les autres nœuds", () => {
      const state = { ...DEFAULT_STATE, ancestralDna: 5 };
      const result = buyEvolutionNode(state, "ponte_amelioree")!;
      const otherNodes = result.evolutionTree.filter((n) => n.id !== "ponte_amelioree");
      expect(otherNodes.every((n) => n.unlocked === false)).toBe(true);
    });
  });

  describe("getClickMultiplierFromTree", () => {
    it("retourne 1 si aucun nœud click débloqué", () => {
      expect(getClickMultiplierFromTree(DEFAULT_EVOLUTION_TREE)).toBe(1);
    });

    it("retourne 2 si Ponte Améliorée débloquée", () => {
      const tree = DEFAULT_EVOLUTION_TREE.map((n) =>
        n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
      );
      expect(getClickMultiplierFromTree(tree)).toBe(2);
    });
  });

  describe("getProductionMultiplierFromTree", () => {
    it("retourne 1 si aucun nœud production débloqué", () => {
      expect(getProductionMultiplierFromTree(DEFAULT_EVOLUTION_TREE)).toBe(1);
    });

    it("retourne 1.5 si Instinct Grégaire débloqué", () => {
      const tree = DEFAULT_EVOLUTION_TREE.map((n) =>
        n.id === "instinct_gregaire" ? { ...n, unlocked: true } : n
      );
      expect(getProductionMultiplierFromTree(tree)).toBe(1.5);
    });
  });

  describe("getStartBonusFromTree", () => {
    it("retourne 0 si aucun nœud start débloqué", () => {
      expect(getStartBonusFromTree(DEFAULT_EVOLUTION_TREE)).toBe(0);
    });

    it("retourne 100 si Mémoire Génétique débloquée", () => {
      const tree = DEFAULT_EVOLUTION_TREE.map((n) =>
        n.id === "memoire_genetique" ? { ...n, unlocked: true } : n
      );
      expect(getStartBonusFromTree(tree)).toBe(100);
    });
  });
});
