import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, act, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import {
  GameProgressProvider,
  useGameProgress,
} from "../GameProgressContext";

// ── helpers ──────────────────────────────────────────────────────────────────

const GAME_CONFIG = {
  gameId: "test-game",
  fases: [{ id: 1 }, { id: 2 }, { id: 3 }],
};

function Consumer() {
  const ctx = useGameProgress();
  return (
    <div>
      <span data-testid="phase">{ctx.currentPhase}</span>
      <span data-testid="completed">{JSON.stringify(ctx.completedPhases)}</span>
      <button onClick={() => ctx.changePhase(2)}>change</button>
      <button onClick={() => ctx.resetProgress()}>reset</button>
      <button onClick={() => ctx.setCompletedPhases([1, 2])}>setCompleted</button>
      <button onClick={() => ctx.setCurrentPhase(3)}>setPhase</button>
    </div>
  );
}

function renderProvider(gameConfig = GAME_CONFIG) {
  return render(
    <GameProgressProvider gameConfig={gameConfig}>
      <Consumer />
    </GameProgressProvider>
  );
}

// ── useGameProgress outside provider ─────────────────────────────────────────

describe("useGameProgress", () => {
  it("throws quando usado fora do provider", () => {
    const spy = vi.spyOn(console, "error").mockImplementation(() => {});
    expect(() => render(<Consumer />)).toThrow(
      "useGameProgress deve ser usado dentro de GameProgressProvider"
    );
    spy.mockRestore();
  });
});

// ── initial state ─────────────────────────────────────────────────────────────

describe("GameProgressProvider — estado inicial", () => {
  beforeEach(() => {
    localStorage.clear();
    delete window.location;
    window.location = { search: "" };
  });

  it("fase inicial é 1 e completedPhases é []", () => {
    renderProvider();
    expect(screen.getByTestId("phase").textContent).toBe("1");
    expect(screen.getByTestId("completed").textContent).toBe("[]");
  });
});

// ── localStorage load ──────────────────────────────────────────────────────────

describe("GameProgressProvider — carregamento do localStorage", () => {
  beforeEach(() => {
    localStorage.clear();
    delete window.location;
    window.location = { search: "" };
  });

  it("carrega fases completadas do localStorage", async () => {
    localStorage.setItem("test-game-completed-phases", JSON.stringify([1, 2]));
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[1,2]");
    });
  });

  it("define currentPhase como a maior fase desbloqueada", async () => {
    // completed=[1,2] → unlocked=[1,2,3] → max=3
    localStorage.setItem("test-game-completed-phases", JSON.stringify([1, 2]));
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("phase").textContent).toBe("3");
    });
  });

  it("migra da chave legada quando storageKey não existe", async () => {
    localStorage.setItem("test-game-fases-concluidas", JSON.stringify([1]));
    const spy = vi.spyOn(console, "info").mockImplementation(() => {});
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[1]");
    });
    // Novo key deve ter sido gravado
    expect(localStorage.getItem("test-game-completed-phases")).toBe("[1]");
    spy.mockRestore();
  });

  it("trata erro de JSON inválido no localStorage sem quebrar", async () => {
    localStorage.setItem("test-game-completed-phases", "INVALID JSON{{");
    const spy = vi.spyOn(console, "error").mockImplementation(() => {});
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[]");
    });
    expect(spy).toHaveBeenCalledWith(
      expect.stringContaining("Migration Error"),
      expect.any(Error)
    );
    spy.mockRestore();
  });
});

// ── debug mode ────────────────────────────────────────────────────────────────

describe("GameProgressProvider — debug mode", () => {
  afterEach(() => {
    localStorage.clear();
  });

  it("em debug=true desbloqueia todas as fases", async () => {
    delete window.location;
    window.location = { search: "?debug=true" };
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[1,2,3]");
    });
    expect(screen.getByTestId("phase").textContent).toBe("3");
  });

  it("debug é case-insensitive (DEBUG=True)", async () => {
    delete window.location;
    window.location = { search: "?DEBUG=True" };
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[1,2,3]");
    });
  });

  it("debug=false não ativa o modo debug", async () => {
    delete window.location;
    window.location = { search: "?debug=false" };
    localStorage.clear();
    renderProvider();
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[]");
    });
  });
});

// ── changePhase ───────────────────────────────────────────────────────────────

describe("GameProgressProvider — changePhase", () => {
  beforeEach(() => {
    localStorage.clear();
    delete window.location;
    window.location = { search: "" };
  });

  it("atualiza currentPhase", async () => {
    const user = userEvent.setup();
    renderProvider();
    await user.click(screen.getByText("change"));
    expect(screen.getByTestId("phase").textContent).toBe("2");
  });
});

// ── resetProgress ──────────────────────────────────────────────────────────────

describe("GameProgressProvider — resetProgress", () => {
  beforeEach(() => {
    localStorage.clear();
    delete window.location;
    window.location = { search: "" };
  });

  it("volta fase para 1 e limpa completedPhases", async () => {
    const user = userEvent.setup();
    localStorage.setItem("test-game-completed-phases", JSON.stringify([1]));
    renderProvider();
    // aguarda carga
    await waitFor(() => {
      expect(screen.getByTestId("completed").textContent).toBe("[1]");
    });
    await user.click(screen.getByText("reset"));
    expect(screen.getByTestId("phase").textContent).toBe("1");
    expect(screen.getByTestId("completed").textContent).toBe("[]");
    expect(localStorage.getItem("test-game-completed-phases")).toBe("[]");
  });
});

// ── setCompletedPhases persiste ───────────────────────────────────────────────

describe("GameProgressProvider — persistência ao mudar completedPhases", () => {
  beforeEach(() => {
    localStorage.clear();
    delete window.location;
    window.location = { search: "" };
  });

  it("persiste no localStorage quando completedPhases muda (após init)", async () => {
    const user = userEvent.setup();
    // Primeiro carregamento: init é falso, efeito de persistência não dispara
    localStorage.setItem("test-game-completed-phases", JSON.stringify([1]));
    renderProvider();
    await waitFor(() =>
      expect(screen.getByTestId("completed").textContent).toBe("[1]")
    );
    // Trigger setCompletedPhases → 2º efeito persiste
    await user.click(screen.getByText("setCompleted"));
    await waitFor(() => {
      expect(localStorage.getItem("test-game-completed-phases")).toBe("[1,2]");
    });
  });
});
