import { logger } from "@infra/logger";
import * as jobsRepo from "@server/repositories/jobs";
import * as settingsRepo from "@server/repositories/settings";
import { scoreJobSuitability } from "@server/services/scorer";
import * as visaSponsors from "@server/services/visa-sponsors/index";
import { asyncPool } from "@server/utils/async-pool";
import {
  detectSponsorshipSignals,
  serializeSponsorshipSignals,
} from "@shared/sponsorship-signals";
import type { Job } from "@shared/types";
import { progressHelpers, updateProgress } from "../progress";
import type { ScoredJob } from "./types";

const SCORING_CONCURRENCY = 4;

function buildSponsorshipSignalsUpdate(job: Job): {
  sponsorshipSignals: string | null;
} {
  return {
    sponsorshipSignals: serializeSponsorshipSignals(
      detectSponsorshipSignals({
        jobDescription: job.jobDescription,
        location: job.location,
        companyAddresses: job.companyAddresses,
        companyDescription: job.companyDescription,
      }),
    ),
  };
}

export async function scoreJobsStep(args: {
  profile: Record<string, unknown>;
  ownerProfileId: string;
  shouldCancel?: () => boolean;
}): Promise<{ unprocessedJobs: Job[]; scoredJobs: ScoredJob[] }> {
  logger.info("Running scoring step");
  const unprocessedJobs = await jobsRepo.getUnscoredDiscoveredJobs(
    undefined,
    args.ownerProfileId,
  );

  // Check if auto-skip threshold is configured
  const autoSkipThresholdRaw = await settingsRepo.getSetting(
    "autoSkipScoreThreshold",
  );
  const autoSkipThreshold = autoSkipThresholdRaw
    ? parseInt(autoSkipThresholdRaw, 10)
    : null;

  updateProgress({
    step: "scoring",
    jobsDiscovered: unprocessedJobs.length,
    jobsScored: 0,
    jobsProcessed: 0,
    totalToProcess: 0,
    currentJob: undefined,
  });

  const scoredJobs: ScoredJob[] = [];
  let completed = 0;

  await asyncPool({
    items: unprocessedJobs,
    concurrency: SCORING_CONCURRENCY,
    shouldStop: args.shouldCancel,
    task: async (job) => {
      if (args.shouldCancel?.()) return;

      try {
        const hasCachedScore =
          typeof job.suitabilityScore === "number" &&
          !Number.isNaN(job.suitabilityScore);

        if (hasCachedScore) {
          if (job.sponsorshipSignals == null) {
            await jobsRepo.updateJob(
              job.id,
              buildSponsorshipSignalsUpdate(job),
            );
          }
          completed += 1;
          progressHelpers.scoringJob(
            completed,
            unprocessedJobs.length,
            `${job.title} (cached)`,
          );
          scoredJobs.push({
            ...job,
            suitabilityScore: job.suitabilityScore as number,
            suitabilityReason: job.suitabilityReason ?? "",
          });
          return;
        }

        const { score, reason, analysis } = await scoreJobSuitability(
          job,
          args.profile,
        );
        if (args.shouldCancel?.()) return;

        let sponsorMatchScore = 0;
        let sponsorMatchNames: string | undefined;

        if (job.employer) {
          const sponsorResults = await visaSponsors.searchSponsors(
            job.employer,
            {
              limit: 10,
              minScore: 50,
            },
          );

          const summary =
            visaSponsors.calculateSponsorMatchSummary(sponsorResults);
          sponsorMatchScore = summary.sponsorMatchScore;
          sponsorMatchNames = summary.sponsorMatchNames ?? undefined;
        }

        const shouldAutoSkip =
          job.status !== "applied" &&
          autoSkipThreshold !== null &&
          !Number.isNaN(autoSkipThreshold) &&
          score < autoSkipThreshold;

        await jobsRepo.updateJob(job.id, {
          suitabilityScore: score,
          suitabilityReason: reason,
          suitabilityAnalysis: analysis ? JSON.stringify(analysis) : undefined,
          sponsorMatchScore,
          sponsorMatchNames,
          ...buildSponsorshipSignalsUpdate(job),
          ...(shouldAutoSkip ? { status: "skipped" } : {}),
        });

        if (shouldAutoSkip) {
          logger.info("Auto-skipped job due to low score", {
            jobId: job.id,
            title: job.title,
            score,
            threshold: autoSkipThreshold,
          });
        }

        completed += 1;
        progressHelpers.scoringJob(
          completed,
          unprocessedJobs.length,
          job.title,
        );
        scoredJobs.push({
          ...job,
          suitabilityScore: score,
          suitabilityReason: reason,
        });
      } catch (error) {
        const message =
          error instanceof Error ? error.message : "Unknown scoring error";
        logger.error("Scoring task failed; continuing with remaining jobs", {
          jobId: job.id,
          title: job.title,
          error: message,
        });
      }
    },
  });

  progressHelpers.scoringComplete(scoredJobs.length);
  logger.info("Scoring step completed", {
    scoredJobs: scoredJobs.length,
    concurrency: SCORING_CONCURRENCY,
  });

  return { unprocessedJobs, scoredJobs };
}
