import { describe, test, expect, mock } from "bun:test";
import { resolveLabels } from "@/pipeline/label-resolver.js";
import type { RepoConfig } from "@/types/index.js";
import type { GitHubClientImpl } from "@/github/client.js";

const WEB_REPO_LABELS = [
  "stg fix",
  "hot fix",
  "talent",
  "comm",
  "ops",
  "people foundation",
  "time managment",
  "data",
  "Tech",
];

const API_REPO_LABELS = ["bugfix", "hotfix", "Talent", "Communication", "Operations"];

const MOBILE_REPO_LABELS = ["🧪 Bugfix", "🚨 Hotfix", "Talent & Data", "Time & People"];

function makeGithubClient(labels: string[]): GitHubClientImpl {
  return { listRepoLabels: async () => labels } as unknown as GitHubClientImpl;
}

function makeRepo(overrides: Partial<RepoConfig> = {}): RepoConfig {
  return {
    name: "test-repo",
    url: "git@github.com:HumandDev/test-repo.git",
    description: "Test repo",
    keywords: ["test"],
    ...overrides,
  };
}

const noopLogger = {
  info: mock(() => {}),
  warn: mock(() => {}),
} as unknown as Parameters<typeof resolveLabels>[4];

function freshLogger() {
  return {
    info: mock(() => {}),
    warn: mock(() => {}),
  } as unknown as Parameters<typeof resolveLabels>[4];
}

// ─── Happy Paths ────────────────────────────────────────────────────────────

describe("resolveLabels — happy paths", () => {
  test("#1 web bugfix — flow + tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(WEB_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["stg fix"], hotfix: ["hot fix"] } }),
      "bugfix",
      "SQDP-100",
      noopLogger,
    );
    expect(result).toContain("stg fix");
    expect(result).toContain("talent");
    expect(result.length).toBe(2);
  });

  test("#2 web hotfix — flow + tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(WEB_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["stg fix"], hotfix: ["hot fix"] } }),
      "hotfix",
      "SQSH-200",
      noopLogger,
    );
    expect(result).toContain("hot fix");
    expect(result).toContain("comm");
    expect(result.length).toBe(2);
  });

  test("#3 web regular — only tribe, no flow label", async () => {
    const result = await resolveLabels(
      makeGithubClient(WEB_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["stg fix"], hotfix: ["hot fix"] } }),
      "regular",
      "SQPD-300",
      noopLogger,
    );
    expect(result).not.toContain("stg fix");
    expect(result).not.toContain("hot fix");
    expect(result).toContain("ops");
  });

  test("#4 api bugfix — flow + tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(API_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["bugfix"], hotfix: ["bugfix", "hotfix"] } }),
      "bugfix",
      "SQDP-400",
      noopLogger,
    );
    expect(result).toContain("bugfix");
    expect(result).toContain("Talent");
  });

  test("#5 mobile hotfix — flow + tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(MOBILE_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["🧪 Bugfix"], hotfix: ["🚨 Hotfix"] } }),
      "hotfix",
      "SQJG-500",
      noopLogger,
    );
    expect(result).toContain("🚨 Hotfix");
    expect(result).toContain("Time & People");
  });
});

// ─── Edge Cases ─────────────────────────────────────────────────────────────

describe("resolveLabels — edge cases", () => {
  test("#6 flow is null — no flow label, only tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(WEB_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["stg fix"], hotfix: ["hot fix"] } }),
      null,
      "SQDP-700",
      noopLogger,
    );
    expect(result).toContain("talent");
    expect(result).not.toContain("stg fix");
    expect(result).not.toContain("hot fix");
  });

  test("#7 repo without prLabels — only tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient(WEB_REPO_LABELS),
      makeRepo(),
      "bugfix",
      "SQDP-800",
      noopLogger,
    );
    expect(result).toContain("talent");
    expect(result.length).toBe(1);
  });

  test("#8 tribe has no matching label in repo", async () => {
    const repoLabels = ["stg fix", "hot fix"];
    const result = await resolveLabels(
      makeGithubClient(repoLabels),
      makeRepo({ prLabels: { bugfix: ["stg fix"] } }),
      "bugfix",
      "SQDP-1100",
      noopLogger,
    );
    expect(result).toEqual(["stg fix"]);
  });

  test("#9 squad without tribe (SQDO) — no tribe label", async () => {
    const result = await resolveLabels(
      makeGithubClient(API_REPO_LABELS),
      makeRepo({ prLabels: { bugfix: ["bugfix"] } }),
      "bugfix",
      "SQDO-1200",
      noopLogger,
    );
    expect(result).toEqual(["bugfix"]);
  });

  test("#10 listRepoLabels fails (returns []) — flow without validation, no tribe", async () => {
    const result = await resolveLabels(
      makeGithubClient([]),
      makeRepo({ prLabels: { bugfix: ["stg fix"] } }),
      "bugfix",
      "SQDP-1300",
      noopLogger,
    );
    expect(result).toContain("stg fix");
    expect(result).not.toContain("talent");
    expect(result.length).toBe(1);
  });

  test("#11 flow label from config does not exist in repo — discarded", async () => {
    const repoLabels = ["talent", "comm"];
    const logger = freshLogger();
    const result = await resolveLabels(
      makeGithubClient(repoLabels),
      makeRepo({ prLabels: { bugfix: ["stg fix"] } }),
      "bugfix",
      "SQDP-1400",
      logger,
    );
    expect(result).not.toContain("stg fix");
    expect(logger.warn).toHaveBeenCalledWith(
      { flow: "bugfix", label: "stg fix" },
      "flow_label_not_in_repo",
    );
  });

  test("#12 flow null with prLabels.default configured — no default labels added", async () => {
    const repoLabels = ["needs-review", "talent"];
    const result = await resolveLabels(
      makeGithubClient(repoLabels),
      makeRepo({ prLabels: { bugfix: ["stg fix"], default: ["needs-review"] } }),
      null,
      "SQDP-1500",
      noopLogger,
    );
    expect(result).not.toContain("needs-review");
    expect(result).toContain("talent");
    expect(result.length).toBe(1);
  });
});
