import type { DuplicateDismissReason } from "@client/lib/job-dedup";
import { jobMatchesAllowedCountry } from "@shared/blocked-countries";
import { textMatchesKeyword } from "@shared/keyword-match";
import type { SponsorshipSignalId } from "@shared/sponsorship-signals";
import {
  jobHasAnySponsorshipSignal,
  jobHasRedSponsorshipFlag,
} from "@shared/sponsorship-signals";
import type { JobListItem, JobSource } from "@shared/types";
import { useMemo } from "react";
import type {
  FilterTab,
  JobSort,
  SalaryFilter,
  SponsorFilter,
  WorkplaceFilter,
} from "./constants";
import { compareJobs, parseSalaryBounds } from "./utils";

export type JobListFilterExtras = {
  foundAfterYmd: string | null;
  foundBeforeYmd: string | null;
  employerInclude: string[];
  employerExclude: string[];
  settingsBlockedEmployerKeywords: string[];
  /** When settings search geography is a country (e.g. Canada), hide other countries. */
  searchGeographyCountryKey?: string | null;
  /** Hide open jobs that match a prior skip/apply (same company + title/description). */
  duplicateDismissHints?: ReadonlyMap<string, DuplicateDismissReason>;
};

const startOfLocalDayMs = (ymd: string): number =>
  new Date(`${ymd}T00:00:00`).getTime();

const endOfLocalDayMs = (ymd: string): number =>
  new Date(`${ymd}T23:59:59.999`).getTime();

function jobMatchesAnyBlockedKeyword(
  job: Pick<JobListItem, "employer" | "title">,
  keywords: string[],
): boolean {
  if (keywords.length === 0) return false;
  return keywords.some(
    (keyword) =>
      textMatchesKeyword(job.employer, keyword) ||
      textMatchesKeyword(job.title, keyword),
  );
}

const getSponsorCategory = (score: number | null): SponsorFilter => {
  if (score == null) return "unknown";
  if (score >= 95) return "confirmed";
  if (score >= 80) return "potential";
  return "not_found";
};

export const useFilteredJobs = (
  jobs: JobListItem[],
  activeTab: FilterTab,
  sourcesFilter: JobSource[],
  sourcesExcludeFilter: JobSource[],
  countriesFilter: string[],
  countriesExcludeFilter: string[],
  sponsorFilter: SponsorFilter,
  sponsorshipSignalsFilter: SponsorshipSignalId[],
  hideSponsorBlockers: boolean,
  workplaceFilter: WorkplaceFilter,
  salaryFilter: SalaryFilter,
  sort: JobSort,
  listExtras: JobListFilterExtras = {
    foundAfterYmd: null,
    foundBeforeYmd: null,
    employerInclude: [],
    employerExclude: [],
    settingsBlockedEmployerKeywords: [],
    searchGeographyCountryKey: null,
    duplicateDismissHints: undefined,
  },
) =>
  useMemo(() => {
    const employerIncludeLower = listExtras.employerInclude.map((value) =>
      value.toLowerCase(),
    );
    const employerExcludeLower = listExtras.employerExclude.map((value) =>
      value.toLowerCase(),
    );
    const settingsBlockedKeywords = listExtras.settingsBlockedEmployerKeywords;
    const uniqueEmployerExcludeLower = [...new Set(employerExcludeLower)];

    let filtered = [...jobs];

    if (activeTab === "ready") {
      filtered = filtered.filter(
        (job) => job.status === "ready" || job.status === "processing",
      );
    } else if (activeTab === "discovered") {
      filtered = filtered.filter(
        (job) => job.status === "discovered" || job.status === "processing",
      );
    } else if (activeTab === "applied") {
      filtered = filtered.filter((job) => job.status === "applied");
    } else if (activeTab === "all") {
      filtered = filtered.filter((job) => job.closedAt == null);
    }

    if (activeTab !== "all") {
      filtered = filtered.filter((job) => job.closedAt == null);
    }

    const duplicateHints = listExtras.duplicateDismissHints;
    if (duplicateHints && duplicateHints.size > 0) {
      filtered = filtered.filter((job) => !duplicateHints.has(job.id));
    }

    if (sourcesFilter.length > 0) {
      const allow = new Set(sourcesFilter);
      filtered = filtered.filter((job) => allow.has(job.source));
    }

    if (sourcesExcludeFilter.length > 0) {
      const deny = new Set(sourcesExcludeFilter);
      filtered = filtered.filter((job) => !deny.has(job.source));
    }

    const searchCountryKey = listExtras.searchGeographyCountryKey;
    if (searchCountryKey) {
      filtered = filtered.filter((job) =>
        jobMatchesAllowedCountry(
          { location: job.location, title: job.title },
          searchCountryKey,
        ),
      );
    }

    if (countriesFilter.length > 0) {
      filtered = filtered.filter((job) =>
        countriesFilter.some((countryKey) =>
          jobMatchesAllowedCountry(
            { location: job.location, title: job.title },
            countryKey,
          ),
        ),
      );
    }

    if (countriesExcludeFilter.length > 0) {
      filtered = filtered.filter(
        (job) =>
          !countriesExcludeFilter.some((countryKey) =>
            jobMatchesAllowedCountry(
              { location: job.location, title: job.title },
              countryKey,
            ),
          ),
      );
    }

    if (sponsorFilter !== "all") {
      filtered = filtered.filter(
        (job) => getSponsorCategory(job.sponsorMatchScore) === sponsorFilter,
      );
    }

    if (sponsorshipSignalsFilter.length > 0) {
      filtered = filtered.filter((job) =>
        jobHasAnySponsorshipSignal(
          job.sponsorshipSignals,
          sponsorshipSignalsFilter,
        ),
      );
    }

    if (hideSponsorBlockers) {
      filtered = filtered.filter(
        (job) => !jobHasRedSponsorshipFlag(job.sponsorshipSignals),
      );
    }

    if (workplaceFilter !== "all") {
      filtered = filtered.filter((job) => {
        if (workplaceFilter === "remote") return job.isRemote === true;
        if (workplaceFilter === "not_remote") return job.isRemote === false;
        return job.isRemote === null;
      });
    }

    const hasMin =
      typeof salaryFilter.min === "number" &&
      Number.isFinite(salaryFilter.min) &&
      salaryFilter.min > 0;
    const hasMax =
      typeof salaryFilter.max === "number" &&
      Number.isFinite(salaryFilter.max) &&
      salaryFilter.max > 0;

    if (listExtras.foundAfterYmd || listExtras.foundBeforeYmd) {
      filtered = filtered.filter((job) => {
        const jobMs = Date.parse(job.discoveredAt);
        if (!Number.isFinite(jobMs)) return false;
        if (
          listExtras.foundAfterYmd &&
          jobMs < startOfLocalDayMs(listExtras.foundAfterYmd)
        ) {
          return false;
        }
        if (
          listExtras.foundBeforeYmd &&
          jobMs > endOfLocalDayMs(listExtras.foundBeforeYmd)
        ) {
          return false;
        }
        return true;
      });
    }

    if (employerIncludeLower.length > 0) {
      filtered = filtered.filter((job) =>
        employerIncludeLower.some((keyword) =>
          job.employer.toLowerCase().includes(keyword),
        ),
      );
    }

    if (settingsBlockedKeywords.length > 0) {
      filtered = filtered.filter(
        (job) => !jobMatchesAnyBlockedKeyword(job, settingsBlockedKeywords),
      );
    }

    if (uniqueEmployerExcludeLower.length > 0) {
      filtered = filtered.filter(
        (job) =>
          !uniqueEmployerExcludeLower.some((keyword) =>
            job.employer.toLowerCase().includes(keyword),
          ),
      );
    }

    if (
      (salaryFilter.mode === "at_least" && hasMin) ||
      (salaryFilter.mode === "at_most" && hasMax) ||
      (salaryFilter.mode === "between" && (hasMin || hasMax))
    ) {
      filtered = filtered.filter((job) => {
        const bounds = parseSalaryBounds(job);
        if (!bounds) return false;

        if (salaryFilter.mode === "at_least") {
          return hasMin ? bounds.max >= (salaryFilter.min as number) : true;
        }

        if (salaryFilter.mode === "at_most") {
          return hasMax ? bounds.min <= (salaryFilter.max as number) : true;
        }

        const min = hasMin ? (salaryFilter.min as number) : null;
        const max = hasMax ? (salaryFilter.max as number) : null;

        if (min != null && max != null) {
          return bounds.max >= min && bounds.min <= max;
        }
        if (min != null) return bounds.max >= min;
        if (max != null) return bounds.min <= max;
        return true;
      });
    }

    return [...filtered].sort((a, b) => compareJobs(a, b, sort));
  }, [
    jobs,
    activeTab,
    sourcesFilter,
    sourcesExcludeFilter,
    countriesFilter,
    countriesExcludeFilter,
    sponsorFilter,
    sponsorshipSignalsFilter,
    hideSponsorBlockers,
    workplaceFilter,
    salaryFilter,
    sort,
    listExtras,
  ]);
