import { describe, expect, it } from "vitest";
import {
  AUTOMATIC_PRESETS,
  buildMaxCoverageDiscoveryLimits,
  calculateAutomaticEstimate,
  deriveExtractorLimits,
  mergeDiscoverySearchTerms,
  parseSearchTermsInput,
} from "./automatic-run";

describe("automatic-run utilities", () => {
  it("exposes the expected preset values", () => {
    expect(AUTOMATIC_PRESETS.fast).toEqual({
      topN: 5,
      minSuitabilityScore: 75,
      runBudget: 300,
    });

    expect(AUTOMATIC_PRESETS.detailed.topN).toBeGreaterThan(
      AUTOMATIC_PRESETS.fast.topN,
    );
  });

  it("calculates estimate range with source caps and topN clipping", () => {
    const estimate = calculateAutomaticEstimate({
      values: {
        topN: 10,
        minSuitabilityScore: 50,
        searchTerms: ["backend", "platform"],
        activeKeywordSetId: null,
        runBudget: 100,
        country: "united kingdom",
        cityLocations: [],
        workplaceTypes: ["remote", "hybrid", "onsite"],
      },
      sources: ["indeed", "linkedin", "gradcracker", "ukvisajobs"],
    });

    expect(estimate.discovered.cap).toBe(100);
    expect(estimate.discovered.min).toBe(35);
    expect(estimate.discovered.max).toBe(75);
    expect(estimate.processed.min).toBe(10);
    expect(estimate.processed.max).toBe(10);
  });

  it("keeps discovered cap under budget regardless of search-term count", () => {
    const limits = deriveExtractorLimits({
      budget: 750,
      searchTerms: ["a", "b", "c"],
      sources: ["indeed", "linkedin", "glassdoor", "gradcracker"],
    });

    const cap =
      3 * limits.jobspyResultsWanted * 3 + limits.gradcrackerMaxJobsPerTerm * 3;

    expect(cap).toBeLessThanOrEqual(750);
  });

  it("assigns a dedicated startupjobs max-jobs limit", () => {
    const limits = deriveExtractorLimits({
      budget: 120,
      searchTerms: ["backend", "platform"],
      sources: ["startupjobs"],
    });

    expect(limits.startupjobsMaxJobsPerTerm).toBeGreaterThan(0);
    expect(limits.startupjobsMaxJobsPerTerm).toBeLessThanOrEqual(120);
  });

  it("returns zero estimate when no search terms are provided", () => {
    const estimate = calculateAutomaticEstimate({
      values: {
        topN: 10,
        minSuitabilityScore: 50,
        searchTerms: [],
        activeKeywordSetId: null,
        runBudget: 750,
        country: "united kingdom",
        cityLocations: [],
        workplaceTypes: ["remote", "hybrid", "onsite"],
      },
      sources: ["indeed", "linkedin", "gradcracker", "ukvisajobs"],
    });

    expect(estimate).toEqual({
      discovered: { min: 0, max: 0, cap: 0 },
      processed: { min: 0, max: 0 },
    });
  });

  it("parses comma and newline separated search terms", () => {
    expect(parseSearchTermsInput("backend, platform\napi\n\n")).toEqual([
      "backend",
      "platform",
      "api",
    ]);
  });

  it("merges profile target roles after saved terms without duplicates", () => {
    expect(
      mergeDiscoverySearchTerms(
        ["Backend Engineer", "  "],
        ["SRE", "backend engineer", " Platform"],
      ),
    ).toEqual(["Backend Engineer", "SRE", "Platform"]);
  });

  it("treats empty saved terms as eligible for profile-only roles", () => {
    expect(mergeDiscoverySearchTerms([], ["Rust", "  "])).toEqual(["Rust"]);
  });

  it("includes adzuna in estimate caps", () => {
    const estimate = calculateAutomaticEstimate({
      values: {
        topN: 10,
        minSuitabilityScore: 50,
        searchTerms: ["backend", "platform"],
        activeKeywordSetId: null,
        runBudget: 120,
        country: "united kingdom",
        cityLocations: [],
        workplaceTypes: ["remote", "hybrid", "onsite"],
      },
      sources: ["adzuna"],
    });

    expect(estimate.discovered.cap).toBeGreaterThan(0);
    expect(estimate.discovered.cap).toBeLessThanOrEqual(120);
  });

  it("includes hiringcafe in estimate caps using the shared term budget", () => {
    const estimate = calculateAutomaticEstimate({
      values: {
        topN: 10,
        minSuitabilityScore: 50,
        searchTerms: ["backend", "platform"],
        activeKeywordSetId: null,
        runBudget: 120,
        country: "united kingdom",
        cityLocations: [],
        workplaceTypes: ["remote", "hybrid", "onsite"],
      },
      sources: ["hiringcafe"],
    });

    expect(estimate.discovered.cap).toBeGreaterThan(0);
    expect(estimate.discovered.cap).toBeLessThanOrEqual(120);
  });

  it("includes startupjobs in estimate caps using the shared term budget", () => {
    const estimate = calculateAutomaticEstimate({
      values: {
        topN: 10,
        minSuitabilityScore: 50,
        searchTerms: ["backend", "platform"],
        activeKeywordSetId: null,
        runBudget: 120,
        country: "united kingdom",
        cityLocations: [],
        workplaceTypes: ["remote", "hybrid", "onsite"],
      },
      sources: ["startupjobs"],
    });

    expect(estimate.discovered.cap).toBeGreaterThan(0);
    expect(estimate.discovered.cap).toBeLessThanOrEqual(120);
  });

  it("raises discovery caps for max coverage runs", () => {
    const limits = buildMaxCoverageDiscoveryLimits({
      budget: 500,
      searchTerms: ["qa engineer"],
      sources: ["workingnomads", "testdevjobs", "indeed"],
    });

    expect(limits.workingnomadsMaxJobsPerTerm).toBeGreaterThanOrEqual(100);
    expect(limits.testdevjobsMaxPages).toBe(10);
    expect(limits.jobspyResultsWanted).toBeGreaterThanOrEqual(100);
  });
});
