import "reflect-metadata";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { NoopTracer, NoopLogger } from "@repo/core-shared/instrumentation";
import { RecordingEventBus, RecordingJobQueue } from "@repo/core-testing/instrumentation";
import { bindDevSeedNavigation } from "@/di/bind-dev-seed";
import { navigationContainer } from "@/di/container";
import { NAVIGATION_SYMBOLS } from "@/di/symbols";
import { MockHeaderRepository } from "@/infrastructure/repositories/header.repository.mock";
import type { IHeaderRepository } from "@/application/repositories/header.repository.interface";

const noop = { tracer: new NoopTracer(), logger: new NoopLogger() };

describe("bindDevSeedNavigation", () => {
  // Each test starts from the default mock binding and tears down afterwards
  // so the global navigationContainer state stays clean for siblings.
  beforeEach(() => {
    if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
      navigationContainer.unbind(NAVIGATION_SYMBOLS.IHeaderRepository);
    }
    navigationContainer
      .bind<IHeaderRepository>(NAVIGATION_SYMBOLS.IHeaderRepository)
      .to(MockHeaderRepository);
  });

  afterEach(() => {
    if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
      navigationContainer.unbind(NAVIGATION_SYMBOLS.IHeaderRepository);
    }
    navigationContainer
      .bind<IHeaderRepository>(NAVIGATION_SYMBOLS.IHeaderRepository)
      .to(MockHeaderRepository);
  });

  it("populates the header repository with the dev header", async () => {
    await bindDevSeedNavigation({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });

    const repo = navigationContainer.get<IHeaderRepository>(
      NAVIGATION_SYMBOLS.IHeaderRepository,
    );
    const header = await repo.getHeader();

    expect(header).toBeDefined();
  });

  it("seeds a header with a non-empty items array", async () => {
    await bindDevSeedNavigation({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });

    const repo = navigationContainer.get<IHeaderRepository>(
      NAVIGATION_SYMBOLS.IHeaderRepository,
    );
    const header = await repo.getHeader();

    expect(header.items.length).toBeGreaterThan(0);
    const homeItem = header.items.find((item) => item.label === "Home");
    expect(homeItem).toBeDefined();
    expect(homeItem?.href).toBe("/");
  });

  it("is idempotent — calling twice rebuilds a fresh populated repo", async () => {
    await bindDevSeedNavigation({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });
    const before = navigationContainer.get<IHeaderRepository>(
      NAVIGATION_SYMBOLS.IHeaderRepository,
    );
    const beforeHeader = await before.getHeader();

    await bindDevSeedNavigation({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });
    const after = navigationContainer.get<IHeaderRepository>(
      NAVIGATION_SYMBOLS.IHeaderRepository,
    );
    const afterHeader = await after.getHeader();

    expect(afterHeader.items.length).toBe(beforeHeader.items.length);
    // It's a fresh instance — not the previous one.
    expect(after).not.toBe(before);
  });
});
