import { serializeSponsorshipSignals } from "@shared/sponsorship-signals";
import { createJob } from "@shared/testing/factories";
import type { Job } from "@shared/types";
import { renderHook } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { useFilteredJobs } from "./useFilteredJobs";

const baseJob = createJob({
  id: "job-1",
  source: "linkedin",
  title: "Engineer",
  employer: "Acme",
  location: "London",
  jobDescription: "Desc",
  status: "ready",
});

describe("useFilteredJobs", () => {
  it("keeps processing jobs visible in the all jobs tab", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "in-progress", status: "in_progress" },
      { ...baseJob, id: "processing", status: "processing" },
      {
        ...baseJob,
        id: "closed",
        status: "in_progress",
        closedAt: 1741996800,
      },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        {
          key: "score",
          direction: "desc",
        },
      ),
    );

    expect(result.current.map((job) => job.id)).toEqual([
      "in-progress",
      "processing",
    ]);
  });

  it("keeps processing jobs visible in the ready tab", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "ready", status: "ready" },
      { ...baseJob, id: "processing", status: "processing" },
      { ...baseJob, id: "discovered", status: "discovered" },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "ready",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        {
          key: "score",
          direction: "desc",
        },
      ),
    );

    expect(result.current.map((job) => job.id)).toEqual(
      expect.arrayContaining(["ready", "processing"]),
    );
    expect(result.current).toHaveLength(2);
  });

  it("filters by sponsor status categories", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "confirmed", sponsorMatchScore: 99 },
      { ...baseJob, id: "potential", sponsorMatchScore: 82 },
      { ...baseJob, id: "not-found", sponsorMatchScore: 45 },
      { ...baseJob, id: "unknown", sponsorMatchScore: null },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "confirmed",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        {
          key: "score",
          direction: "desc",
        },
      ),
    );

    expect(result.current.map((job) => job.id)).toEqual(["confirmed"]);
  });

  it("filters by sponsorship text signals", () => {
    const jobs: Job[] = [
      {
        ...baseJob,
        id: "tn",
        sponsorshipSignals: serializeSponsorshipSignals(["tn_eligible"]),
      },
      {
        ...baseJob,
        id: "sponsor",
        sponsorshipSignals: serializeSponsorshipSignals(["will_sponsor"]),
      },
      { ...baseJob, id: "none", sponsorshipSignals: null },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        ["tn_eligible", "will_sponsor"],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );

    expect(result.current.map((job) => job.id).sort()).toEqual([
      "sponsor",
      "tn",
    ]);
  });

  it("hides red-flag jobs but keeps yellow no-sponsor listings when enabled", () => {
    const jobs: Job[] = [
      {
        ...baseJob,
        id: "red",
        sponsorshipSignals: serializeSponsorshipSignals([
          "citizenship_required",
        ]),
      },
      {
        ...baseJob,
        id: "yellow",
        sponsorshipSignals: serializeSponsorshipSignals([
          "no_sponsorship_stated",
        ]),
      },
      {
        ...baseJob,
        id: "tn",
        sponsorshipSignals: serializeSponsorshipSignals(["tn_eligible"]),
      },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        true,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );

    expect(result.current.map((job) => job.id).sort()).toEqual([
      "tn",
      "yellow",
    ]);
  });

  it("filters by salary range using structured and text salary fields", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "structured", salaryMinAmount: 70000 },
      { ...baseJob, id: "k-format", salary: "GBP 65k" },
      { ...baseJob, id: "below", salary: "GBP 55k" },
      { ...baseJob, id: "none", salary: null },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "between", min: 60000, max: 80000 },
        {
          key: "score",
          direction: "desc",
        },
      ),
    );

    expect(result.current.map((job) => job.id)).toEqual(
      expect.arrayContaining(["structured", "k-format"]),
    );
    expect(result.current).toHaveLength(2);
  });

  it("sorts by salary with highest first and missing salaries last", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "max", salaryMinAmount: 120000 },
      { ...baseJob, id: "mid", salary: "GBP 65k" },
      { ...baseJob, id: "low", salaryMinAmount: 50000 },
      { ...baseJob, id: "none", salary: null, salaryMinAmount: null },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        {
          key: "salary",
          direction: "desc",
        },
      ),
    );

    expect(result.current.map((job) => job.id)).toEqual([
      "max",
      "mid",
      "low",
      "none",
    ]);
  });

  it("filters by remote workplace flag", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "remote", isRemote: true },
      { ...baseJob, id: "onsite", isRemote: false },
      { ...baseJob, id: "unknown", isRemote: null },
    ];

    const { result: remoteOnly } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "remote",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(remoteOnly.current.map((j) => j.id)).toEqual(["remote"]);

    const { result: notRemote } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "not_remote",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(notRemote.current.map((j) => j.id)).toEqual(["onsite"]);

    const { result: unknown } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "unknown",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(unknown.current.map((j) => j.id)).toEqual(["unknown"]);
  });

  it("allows multiple source filters", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "li", source: "linkedin" },
      { ...baseJob, id: "in", source: "indeed" },
      { ...baseJob, id: "gc", source: "gradcracker" },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        ["linkedin", "indeed"],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(result.current.map((j) => j.id).sort()).toEqual(["in", "li"]);
  });

  it("filters by country including remote jobs when location matches", () => {
    const jobs: Job[] = [
      {
        ...baseJob,
        id: "uk-onsite",
        location: "London, UK",
        isRemote: false,
      },
      {
        ...baseJob,
        id: "us-remote",
        location: "New York, NY, United States",
        isRemote: true,
      },
      {
        ...baseJob,
        id: "us-onsite",
        location: "New York, NY, United States",
        isRemote: false,
      },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        ["united kingdom"],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(result.current.map((j) => j.id)).toEqual(["uk-onsite"]);
  });

  it("hides jobs outside search geography country from settings", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "ca", location: "Toronto, ON, Canada" },
      { ...baseJob, id: "in", location: "Bangalore, India", isRemote: true },
      { ...baseJob, id: "vague", location: "Remote", isRemote: true },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: null,
          foundBeforeYmd: null,
          employerInclude: [],
          employerExclude: [],
          settingsBlockedEmployerKeywords: [],
          searchGeographyCountryKey: "canada",
        },
      ),
    );
    expect(result.current.map((j) => j.id)).toEqual(["ca"]);
  });

  it("excludes by country including remote jobs in that country", () => {
    const jobs: Job[] = [
      {
        ...baseJob,
        id: "uk-onsite",
        location: "London, UK",
        isRemote: false,
      },
      {
        ...baseJob,
        id: "uk-remote",
        location: "London, UK",
        isRemote: true,
      },
      {
        ...baseJob,
        id: "us-onsite",
        location: "New York, United States",
        isRemote: false,
      },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        ["united kingdom"],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(result.current.map((j) => j.id)).toEqual(["us-onsite"]);
  });

  it("excludes sources", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "li", source: "linkedin" },
      { ...baseJob, id: "in", source: "indeed" },
    ];
    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        ["linkedin"],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
      ),
    );
    expect(result.current.map((j) => j.id)).toEqual(["in"]);
  });

  it("filters by discovered date range in local time", () => {
    const jobs: Job[] = [
      {
        ...baseJob,
        id: "early",
        discoveredAt: "2026-01-10T12:00:00.000Z",
      },
      {
        ...baseJob,
        id: "late",
        discoveredAt: "2026-02-20T12:00:00.000Z",
      },
    ];

    const { result } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: "2026-02-01",
          foundBeforeYmd: "2026-02-28",
          employerInclude: [],
          employerExclude: [],
          settingsBlockedEmployerKeywords: [],
        },
      ),
    );

    expect(result.current.map((j) => j.id)).toEqual(["late"]);
  });

  it("hides employers matching exclude tokens or settings skip list", () => {
    const jobs: Job[] = [
      { ...baseJob, id: "agency", employer: "Acme Staffing Ltd" },
      { ...baseJob, id: "good", employer: "Contoso" },
    ];

    const { result: localExclude } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: null,
          foundBeforeYmd: null,
          employerInclude: [],
          employerExclude: ["staffing"],
          settingsBlockedEmployerKeywords: [],
        },
      ),
    );
    expect(localExclude.current.map((j) => j.id)).toEqual(["good"]);

    const { result: settingsSkip } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: null,
          foundBeforeYmd: null,
          employerInclude: [],
          employerExclude: [],
          settingsBlockedEmployerKeywords: ["staffing"],
        },
      ),
    );
    expect(settingsSkip.current.map((j) => j.id)).toEqual(["good"]);

    const { result: coOpTitle } = renderHook(() =>
      useFilteredJobs(
        [
          {
            ...baseJob,
            id: "coop",
            employer: "Global Relay Communications Inc.",
            title: "Co-op Software Development Engineer in Test (SDET)",
          },
          { ...baseJob, id: "good", employer: "Contoso", title: "SDET" },
        ],
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: null,
          foundBeforeYmd: null,
          employerInclude: [],
          employerExclude: [],
          settingsBlockedEmployerKeywords: ["co-op"],
        },
      ),
    );
    expect(coOpTitle.current.map((j) => j.id)).toEqual(["good"]);

    const { result: includeOnly } = renderHook(() =>
      useFilteredJobs(
        jobs,
        "all",
        [],
        [],
        [],
        [],
        "all",
        [],
        false,
        "all",
        { mode: "at_least", min: null, max: null },
        { key: "score", direction: "desc" },
        {
          foundAfterYmd: null,
          foundBeforeYmd: null,
          employerInclude: ["contoso"],
          employerExclude: [],
          settingsBlockedEmployerKeywords: [],
        },
      ),
    );
    expect(includeOnly.current.map((j) => j.id)).toEqual(["good"]);
  });
});
