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 { bindDevSeedMarketingPages } from "@/di/bind-dev-seed";
import { marketingPagesContainer } from "@/di/container";
import { MARKETING_PAGES_SYMBOLS } from "@/di/symbols";
import { MockPagesRepository } from "@/infrastructure/repositories/pages.repository.mock";
import { MockSiteSettingsRepository } from "@/infrastructure/repositories/site-settings.repository.mock";
import type { IPagesRepository } from "@/application/repositories/pages.repository.interface";
import type { ISiteSettingsRepository } from "@/application/repositories/site-settings.repository.interface";

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

describe("bindDevSeedMarketingPages", () => {
  // Each test starts from default mock bindings and tears down afterwards so
  // the global marketingPagesContainer state stays clean for siblings.
  beforeEach(() => {
    if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository)) {
      marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IPagesRepository);
    }
    marketingPagesContainer
      .bind<IPagesRepository>(MARKETING_PAGES_SYMBOLS.IPagesRepository)
      .to(MockPagesRepository);

    if (
      marketingPagesContainer.isBound(
        MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository,
      )
    ) {
      marketingPagesContainer.unbind(
        MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository,
      );
    }
    marketingPagesContainer
      .bind<ISiteSettingsRepository>(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository)
      .to(MockSiteSettingsRepository);
  });

  afterEach(() => {
    if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository)) {
      marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IPagesRepository);
    }
    marketingPagesContainer
      .bind<IPagesRepository>(MARKETING_PAGES_SYMBOLS.IPagesRepository)
      .to(MockPagesRepository);

    if (
      marketingPagesContainer.isBound(
        MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository,
      )
    ) {
      marketingPagesContainer.unbind(
        MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository,
      );
    }
    marketingPagesContainer
      .bind<ISiteSettingsRepository>(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository)
      .to(MockSiteSettingsRepository);
  });

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

    const repo = marketingPagesContainer.get<IPagesRepository>(
      MARKETING_PAGES_SYMBOLS.IPagesRepository,
    );
    const pages = await repo.getPages();

    expect(pages.length).toBeGreaterThan(0);
  });

  it("seeds site settings with a non-default site name", async () => {
    await bindDevSeedMarketingPages({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });

    const settingsRepo = marketingPagesContainer.get<ISiteSettingsRepository>(
      MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository,
    );
    const settings = await settingsRepo.getSiteSettings();

    expect(settings.siteName).toBe("My App");
    expect(settings.siteDescription).toBeDefined();
  });

  it("is idempotent — calling twice rebuilds fresh populated repos", async () => {
    await bindDevSeedMarketingPages({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });
    const before = marketingPagesContainer.get<IPagesRepository>(
      MARKETING_PAGES_SYMBOLS.IPagesRepository,
    );
    const beforeCount = (await before.getPages()).length;

    await bindDevSeedMarketingPages({ ...noop, bus: new RecordingEventBus(), queue: new RecordingJobQueue() });
    const after = marketingPagesContainer.get<IPagesRepository>(
      MARKETING_PAGES_SYMBOLS.IPagesRepository,
    );
    const afterCount = (await after.getPages()).length;

    expect(afterCount).toBe(beforeCount);
    // It's a fresh instance — not the previous one.
    expect(after).not.toBe(before);
  });
});
