import { describe, it, expect, vi, beforeEach } from "vitest";
import {
  BlocklyWorkspaceValidator,
  defaultValidator,
  validateBlocklyWorkspace,
} from "../validation";

const makeBlock = (type, overrides = {}) => ({
  type,
  disposed: false,
  setDisabledReason: vi.fn(),
  ...overrides,
});

const makeWorkspace = (blocks) => ({
  getTopBlocks: vi.fn(() => blocks),
});

describe("BlocklyWorkspaceValidator — construtor", () => {
  it("usa defaults quando chamado sem opcoes", () => {
    const v = new BlocklyWorkspaceValidator();
    expect(v.options.allowMultipleTopBlocks).toBe(false);
    expect(v.options.preferredStartBlocks).toEqual(["start", "when_run", "main"]);
  });

  it("mescla opcoes fornecidas sobre os defaults", () => {
    const v = new BlocklyWorkspaceValidator({ allowMultipleTopBlocks: true });
    expect(v.options.allowMultipleTopBlocks).toBe(true);
  });

  it("aceita preferredStartBlocks customizado", () => {
    const v = new BlocklyWorkspaceValidator({ preferredStartBlocks: ["begin"] });
    expect(v.options.preferredStartBlocks).toEqual(["begin"]);
  });
});

describe("BlocklyWorkspaceValidator — validateAndClean()", () => {
  it("retorna isValid:false quando workspace e null", () => {
    const v = new BlocklyWorkspaceValidator();
    const r = v.validateAndClean(null);
    expect(r.isValid).toBe(false);
    expect(r.warnings).toContain("Workspace não disponível");
  });

  it("retorna isValid:false quando nao ha blocos", () => {
    const v = new BlocklyWorkspaceValidator();
    const r = v.validateAndClean(makeWorkspace([]));
    expect(r.isValid).toBe(false);
    expect(r.warnings[0]).toMatch(/nenhum bloco/i);
  });

  it("retorna isValid:true com um unico bloco", () => {
    const v = new BlocklyWorkspaceValidator();
    const block = makeBlock("mover");
    const r = v.validateAndClean(makeWorkspace([block]));
    expect(r.isValid).toBe(true);
    expect(r.warnings).toHaveLength(0);
    expect(r.mainBlock).toBe(block);
    expect(r.reEnableBlocks).toBeNull();
  });

  it("desabilita blocos extras quando ha multiplos top-blocks", () => {
    const v = new BlocklyWorkspaceValidator();
    const b1 = makeBlock("start");
    const b2 = makeBlock("outro");
    const r = v.validateAndClean(makeWorkspace([b1, b2]));
    expect(r.isValid).toBe(true);
    expect(r.mainBlock).toBe(b1);
    expect(b2.setDisabledReason).toHaveBeenCalledWith(true, "MANUALLY_DISABLED");
    expect(r.disabledCount).toBe(1);
    expect(typeof r.reEnableBlocks).toBe("function");
  });

  it("re-habilita blocos ao chamar reEnableBlocks()", () => {
    const v = new BlocklyWorkspaceValidator();
    const b1 = makeBlock("start");
    const b2 = makeBlock("outro");
    const r = v.validateAndClean(makeWorkspace([b1, b2]));
    r.reEnableBlocks();
    expect(b2.setDisabledReason).toHaveBeenCalledWith(false);
  });

  it("nao desabilita blocos quando allowMultipleTopBlocks:true", () => {
    const v = new BlocklyWorkspaceValidator({ allowMultipleTopBlocks: true });
    const b1 = makeBlock("start");
    const b2 = makeBlock("outro");
    const r = v.validateAndClean(makeWorkspace([b1, b2]));
    expect(r.isValid).toBe(true);
    expect(b2.setDisabledReason).not.toHaveBeenCalled();
    expect(r.reEnableBlocks).toBeNull();
  });

  it("emite warning quando ha multiplos programas", () => {
    const v = new BlocklyWorkspaceValidator();
    const r = v.validateAndClean(makeWorkspace([makeBlock("a"), makeBlock("b")]));
    expect(r.warnings.some((w) => /2 programas/i.test(w))).toBe(true);
  });
});

describe("BlocklyWorkspaceValidator — selectMainBlock()", () => {
  it("prefere bloco do tipo 'start'", () => {
    const v = new BlocklyWorkspaceValidator();
    const blocks = [makeBlock("outro"), makeBlock("start"), makeBlock("when_run")];
    expect(v.selectMainBlock(blocks).type).toBe("start");
  });

  it("prefere 'when_run' quando nao ha 'start'", () => {
    const v = new BlocklyWorkspaceValidator();
    const blocks = [makeBlock("outro"), makeBlock("when_run")];
    expect(v.selectMainBlock(blocks).type).toBe("when_run");
  });

  it("retorna primeiro bloco quando nenhum preferido existe", () => {
    const v = new BlocklyWorkspaceValidator();
    const blocks = [makeBlock("primeiro"), makeBlock("segundo")];
    expect(v.selectMainBlock(blocks).type).toBe("primeiro");
  });

  it("ignora blocos disposed", () => {
    const v = new BlocklyWorkspaceValidator();
    const blocks = [makeBlock("start", { disposed: true }), makeBlock("outro")];
    expect(v.selectMainBlock(blocks).type).toBe("outro");
  });

  it("retorna null quando todos os blocos estao disposed", () => {
    const v = new BlocklyWorkspaceValidator();
    const blocks = [makeBlock("a", { disposed: true })];
    expect(v.selectMainBlock(blocks)).toBeNull();
  });

  it("aceita tipo parcial (inclui preferredStartBlock no nome)", () => {
    const v = new BlocklyWorkspaceValidator({ preferredStartBlocks: ["main"] });
    const blocks = [makeBlock("game_main"), makeBlock("outro")];
    expect(v.selectMainBlock(blocks).type).toBe("game_main");
  });
});

describe("BlocklyWorkspaceValidator — isBlockControllable()", () => {
  it("retorna true para bloco com setDisabledReason e nao disposed", () => {
    const v = new BlocklyWorkspaceValidator();
    expect(v.isBlockControllable(makeBlock("x"))).toBe(true);
  });

  it("retorna false para null", () => {
    const v = new BlocklyWorkspaceValidator();
    expect(v.isBlockControllable(null)).toBeFalsy();
  });

  it("retorna false para bloco disposed", () => {
    const v = new BlocklyWorkspaceValidator();
    expect(v.isBlockControllable(makeBlock("x", { disposed: true }))).toBe(false);
  });

  it("retorna false quando falta setDisabledReason", () => {
    const v = new BlocklyWorkspaceValidator();
    expect(v.isBlockControllable({ type: "x", disposed: false })).toBe(false);
  });
});

describe("defaultValidator", () => {
  it("e instancia de BlocklyWorkspaceValidator", () => {
    expect(defaultValidator).toBeInstanceOf(BlocklyWorkspaceValidator);
  });

  it("tem allowMultipleTopBlocks:false", () => {
    expect(defaultValidator.options.allowMultipleTopBlocks).toBe(false);
  });

  it("inclui 'begin' nos preferredStartBlocks", () => {
    expect(defaultValidator.options.preferredStartBlocks).toContain("begin");
  });
});

describe("validateBlocklyWorkspace()", () => {
  it("valida workspace com um bloco", () => {
    const block = makeBlock("start");
    const r = validateBlocklyWorkspace(makeWorkspace([block]));
    expect(r.isValid).toBe(true);
    expect(r.mainBlock).toBe(block);
  });

  it("retorna isValid:false para workspace null", () => {
    const r = validateBlocklyWorkspace(null);
    expect(r.isValid).toBe(false);
  });

  it("passa options para o validador", () => {
    const b1 = makeBlock("a");
    const b2 = makeBlock("b");
    const r = validateBlocklyWorkspace(makeWorkspace([b1, b2]), {
      allowMultipleTopBlocks: true,
    });
    expect(b2.setDisabledReason).not.toHaveBeenCalled();
  });
});
