import * as api from "@client/api";
import { PageHeader } from "@client/components/layout";
import { useUpdateSettingsMutation } from "@client/hooks/queries/useSettingsMutation";
import { DangerZoneSection } from "@client/pages/settings/components/DangerZoneSection";
import { EnvironmentSettingsSection } from "@client/pages/settings/components/EnvironmentSettingsSection";
import { JobSearchProfileSection } from "@client/pages/settings/components/JobSearchProfileSection";
import { JobSourcesSettingsSection } from "@client/pages/settings/components/JobSourcesSettingsSection";
import { KeywordSetsSettingsSection } from "@client/pages/settings/components/KeywordSetsSettingsSection";
import { ModelSettingsSection } from "@client/pages/settings/components/ModelSettingsSection";
import { ScoringSettingsSection } from "@client/pages/settings/components/ScoringSettingsSection";
import { WebhooksSection } from "@client/pages/settings/components/WebhooksSection";
import {
  type LlmProviderId,
  normalizeLlmProvider,
  resumeProjectsEqual,
} from "@client/pages/settings/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { normalizeBlockedCountryTokens } from "@shared/blocked-countries.js";
import { normalizeStringArray } from "@shared/normalize-string-array.js";
import {
  type UpdateSettingsInput,
  updateSettingsSchema,
} from "@shared/settings-schema.js";
import type { AppSettings, JobStatus } from "@shared/types.js";
import { useQuery } from "@tanstack/react-query";
import { Settings } from "lucide-react";
import type React from "react";
import { useEffect, useState } from "react";
import { FormProvider, type Resolver, useForm } from "react-hook-form";
import { toast } from "sonner";
import { useQueryErrorToast } from "@/client/hooks/useQueryErrorToast";
import { queryKeys } from "@/client/lib/queryKeys";
import { Accordion } from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";

const DEFAULT_FORM_VALUES: UpdateSettingsInput = {
  jobSearchProfile: null,
  model: "",
  modelScorer: "",
  modelTailoring: "",
  modelProjectSelection: "",
  llmProvider: null,
  llmBaseUrl: "",
  llmApiKey: "",
  pipelineWebhookUrl: "",
  jobCompleteWebhookUrl: "",
  resumeProjects: null,
  rxresumeMode: "v5",
  rxresumeBaseResumeId: null,
  showSponsorInfo: null,
  renderMarkdownInJobDescriptions: null,
  chatStyleTone: "",
  chatStyleFormality: "",
  chatStyleConstraints: "",
  chatStyleDoNotUse: "",
  chatStyleLanguageMode: null,
  chatStyleManualLanguage: null,
  rxresumeEmail: "",
  rxresumeUrl: "",
  localResumeProfilePath: "",
  rxresumePassword: "",
  rxresumeApiKey: "",
  basicAuthUser: "",
  basicAuthPassword: "",
  ukvisajobsEmail: "",
  ukvisajobsPassword: "",
  adzunaAppId: "",
  adzunaAppKey: "",
  webhookSecret: "",
  enableBasicAuth: false,
  backupEnabled: null,
  backupHour: null,
  backupMaxCount: null,
  penalizeMissingSalary: null,
  missingSalaryPenalty: null,
  autoSkipScoreThreshold: null,
  blockedCompanyKeywords: [],
  blockedCountries: [],
  scoringInstructions: "",
  ashbyCompanies: [],
  greenhouseCompanies: [],
  leverCompanies: [],
  smartrecruitersCompanies: [],
  teamtailorCompanies: [],
  huntflowTenants: [],
  factorialTenants: [],
  workdayTenants: [],
  careersPageUrls: [],
  icimsTenants: [],
  elutaRssLocations: [],
};

type LlmProviderValue = LlmProviderId | null;

const normalizeLlmProviderValue = (
  value: string | null | undefined,
): LlmProviderValue => (value ? normalizeLlmProvider(value) : null);

const NULL_SETTINGS_PAYLOAD: UpdateSettingsInput = {
  jobSearchProfile: null,
  model: null,
  modelScorer: null,
  modelTailoring: null,
  modelProjectSelection: null,
  llmProvider: null,
  llmBaseUrl: null,
  llmApiKey: null,
  pipelineWebhookUrl: null,
  jobCompleteWebhookUrl: null,
  resumeProjects: null,
  rxresumeMode: null,
  rxresumeBaseResumeId: null,
  showSponsorInfo: null,
  renderMarkdownInJobDescriptions: null,
  chatStyleTone: null,
  chatStyleFormality: null,
  chatStyleConstraints: null,
  chatStyleDoNotUse: null,
  chatStyleLanguageMode: null,
  chatStyleManualLanguage: null,
  rxresumeEmail: null,
  rxresumeUrl: null,
  localResumeProfilePath: null,
  rxresumePassword: null,
  rxresumeApiKey: null,
  basicAuthUser: null,
  basicAuthPassword: null,
  ukvisajobsEmail: null,
  ukvisajobsPassword: null,
  adzunaAppId: null,
  adzunaAppKey: null,
  adzunaMaxJobsPerTerm: null,
  webhookSecret: null,
  enableBasicAuth: undefined,
  backupEnabled: null,
  backupHour: null,
  backupMaxCount: null,
  penalizeMissingSalary: null,
  missingSalaryPenalty: null,
  autoSkipScoreThreshold: null,
  blockedCompanyKeywords: null,
  blockedCountries: null,
  scoringInstructions: null,
};

const mapSettingsToForm = (data: AppSettings): UpdateSettingsInput => ({
  jobSearchProfile: data.jobSearchProfile?.override ?? null,
  model: data.model.override ?? "",
  modelScorer: data.modelScorer.override ?? "",
  modelTailoring: data.modelTailoring.override ?? "",
  modelProjectSelection: data.modelProjectSelection.override ?? "",
  llmProvider: normalizeLlmProviderValue(data.llmProvider.override),
  llmBaseUrl: data.llmBaseUrl.override ?? "",
  llmApiKey: "",
  pipelineWebhookUrl: data.pipelineWebhookUrl.override ?? "",
  jobCompleteWebhookUrl: data.jobCompleteWebhookUrl.override ?? "",
  resumeProjects: data.resumeProjects.override,
  rxresumeMode: data.rxresumeMode.override ?? data.rxresumeMode.value,
  rxresumeBaseResumeId: data.rxresumeBaseResumeId,
  showSponsorInfo: data.showSponsorInfo.override,
  renderMarkdownInJobDescriptions:
    data.renderMarkdownInJobDescriptions.override,
  chatStyleTone: data.chatStyleTone.override ?? "",
  chatStyleFormality: data.chatStyleFormality.override ?? "",
  chatStyleConstraints: data.chatStyleConstraints.override ?? "",
  chatStyleDoNotUse: data.chatStyleDoNotUse.override ?? "",
  chatStyleLanguageMode: data.chatStyleLanguageMode.override ?? null,
  chatStyleManualLanguage: data.chatStyleManualLanguage.override ?? null,
  rxresumeEmail: data.rxresumeEmail ?? "",
  rxresumeUrl: data.rxresumeUrl ?? "",
  localResumeProfilePath: data.localResumeProfilePath ?? "",
  rxresumePassword: "",
  rxresumeApiKey: "",
  basicAuthUser: data.basicAuthUser ?? "",
  basicAuthPassword: "",
  ukvisajobsEmail: data.ukvisajobsEmail ?? "",
  ukvisajobsPassword: "",
  adzunaAppId: data.adzunaAppId ?? "",
  adzunaAppKey: "",
  webhookSecret: "",
  enableBasicAuth: data.basicAuthActive,
  backupEnabled: data.backupEnabled.override,
  backupHour: data.backupHour.override,
  backupMaxCount: data.backupMaxCount.override,
  penalizeMissingSalary: data.penalizeMissingSalary.override,
  missingSalaryPenalty: data.missingSalaryPenalty.override,
  autoSkipScoreThreshold: data.autoSkipScoreThreshold.override,
  blockedCompanyKeywords: data.blockedCompanyKeywords.override ?? [],
  blockedCountries: data.blockedCountries.override ?? [],
  scoringInstructions: data.scoringInstructions.override ?? "",
  ashbyCompanies: data.ashbyCompanies.value,
  greenhouseCompanies: data.greenhouseCompanies.value,
  leverCompanies: data.leverCompanies.value,
  smartrecruitersCompanies: data.smartrecruitersCompanies.value,
  teamtailorCompanies: data.teamtailorCompanies.value,
  huntflowTenants: data.huntflowTenants.value,
  factorialTenants: data.factorialTenants.value,
  workdayTenants: data.workdayTenants.value,
  careersPageUrls: data.careersPageUrls.value,
  icimsTenants: data.icimsTenants.value,
  elutaRssLocations: data.elutaRssLocations.value,
});

const normalizeString = (value: string | null | undefined) => {
  const trimmed = value?.trim();
  return trimmed ? trimmed : null;
};

const normalizePrivateInput = (value: string | null | undefined) => {
  const trimmed = value?.trim();
  if (trimmed === "") return null;
  return trimmed || undefined;
};

const stringArraysEqual = (left: string[], right: string[]): boolean => {
  if (left.length !== right.length) return false;
  return left.every((value, index) => value === right[index]);
};

const nullIfSame = <T,>(value: T | null | undefined, defaultValue: T) =>
  value === defaultValue ? null : (value ?? null);

const getDerivedSettings = (settings: AppSettings | null) => {
  return {
    model: {
      effective: settings?.model?.value ?? "",
      default: settings?.model?.default ?? "",
      scorer: settings?.modelScorer?.value ?? "",
      tailoring: settings?.modelTailoring?.value ?? "",
      projectSelection: settings?.modelProjectSelection?.value ?? "",
      llmProvider: settings?.llmProvider?.value ?? "",
      llmBaseUrl: settings?.llmBaseUrl?.value ?? "",
      llmApiKeyHint: settings?.llmApiKeyHint ?? null,
    },
    pipelineWebhook: {
      effective: settings?.pipelineWebhookUrl?.value ?? "",
      default: settings?.pipelineWebhookUrl?.default ?? "",
    },
    jobCompleteWebhook: {
      effective: settings?.jobCompleteWebhookUrl?.value ?? "",
      default: settings?.jobCompleteWebhookUrl?.default ?? "",
    },
    display: {
      showSponsorInfo: {
        effective: settings?.showSponsorInfo?.value ?? true,
        default: settings?.showSponsorInfo?.default ?? true,
      },
      renderMarkdownInJobDescriptions: {
        effective: settings?.renderMarkdownInJobDescriptions?.value ?? true,
        default: settings?.renderMarkdownInJobDescriptions?.default ?? true,
      },
    },
    envSettings: {
      readable: {
        ukvisajobsEmail: settings?.ukvisajobsEmail ?? "",
        adzunaAppId: settings?.adzunaAppId ?? "",
        basicAuthUser: settings?.basicAuthUser ?? "",
      },
      private: {
        ukvisajobsPasswordHint: settings?.ukvisajobsPasswordHint ?? null,
        adzunaAppKeyHint: settings?.adzunaAppKeyHint ?? null,
        basicAuthPasswordHint: settings?.basicAuthPasswordHint ?? null,
        webhookSecretHint: settings?.webhookSecretHint ?? null,
      },
      basicAuthActive: settings?.basicAuthActive ?? false,
    },
    defaultResumeProjects: settings?.resumeProjects?.default ?? null,
    backup: {
      backupEnabled: {
        effective: settings?.backupEnabled?.value ?? false,
        default: settings?.backupEnabled?.default ?? false,
      },
      backupHour: {
        effective: settings?.backupHour?.value ?? 2,
        default: settings?.backupHour?.default ?? 2,
      },
      backupMaxCount: {
        effective: settings?.backupMaxCount?.value ?? 5,
        default: settings?.backupMaxCount?.default ?? 5,
      },
    },
    jobSearchProfile: {
      effective: settings?.jobSearchProfile?.value ?? null,
      default: settings?.jobSearchProfile?.default ?? null,
    },
    scoring: {
      penalizeMissingSalary: {
        effective: settings?.penalizeMissingSalary?.value ?? false,
        default: settings?.penalizeMissingSalary?.default ?? false,
      },
      missingSalaryPenalty: {
        effective: settings?.missingSalaryPenalty?.value ?? 10,
        default: settings?.missingSalaryPenalty?.default ?? 10,
      },
      autoSkipScoreThreshold: {
        effective: settings?.autoSkipScoreThreshold?.value ?? null,
        default: settings?.autoSkipScoreThreshold?.default ?? null,
      },
      blockedCompanyKeywords: {
        effective: settings?.blockedCompanyKeywords?.value ?? [],
        default: settings?.blockedCompanyKeywords?.default ?? [],
      },
      blockedCountries: {
        effective: settings?.blockedCountries?.value ?? [],
        default: settings?.blockedCountries?.default ?? [],
      },
      scoringInstructions: {
        effective: settings?.scoringInstructions?.value ?? "",
        default: settings?.scoringInstructions?.default ?? "",
      },
    },
    jobSources: {
      ashbyCompanies: {
        effective: settings?.ashbyCompanies?.value ?? [],
        default: settings?.ashbyCompanies?.default ?? [],
      },
      greenhouseCompanies: {
        effective: settings?.greenhouseCompanies?.value ?? [],
        default: settings?.greenhouseCompanies?.default ?? [],
      },
      leverCompanies: {
        effective: settings?.leverCompanies?.value ?? [],
        default: settings?.leverCompanies?.default ?? [],
      },
      smartrecruitersCompanies: {
        effective: settings?.smartrecruitersCompanies?.value ?? [],
        default: settings?.smartrecruitersCompanies?.default ?? [],
      },
      teamtailorCompanies: {
        effective: settings?.teamtailorCompanies?.value ?? [],
        default: settings?.teamtailorCompanies?.default ?? [],
      },
      huntflowTenants: {
        effective: settings?.huntflowTenants?.value ?? [],
        default: settings?.huntflowTenants?.default ?? [],
      },
      factorialTenants: {
        effective: settings?.factorialTenants?.value ?? [],
        default: settings?.factorialTenants?.default ?? [],
      },
      workdayTenants: {
        effective: settings?.workdayTenants?.value ?? [],
        default: settings?.workdayTenants?.default ?? [],
      },
      careersPageUrls: {
        effective: settings?.careersPageUrls?.value ?? [],
        default: settings?.careersPageUrls?.default ?? [],
      },
      icimsTenants: {
        effective: settings?.icimsTenants?.value ?? [],
        default: settings?.icimsTenants?.default ?? [],
      },
      elutaRssLocations: {
        effective: settings?.elutaRssLocations?.value ?? [],
        default: settings?.elutaRssLocations?.default ?? [],
      },
    },
  };
};

export const SettingsPage: React.FC = () => {
  const [settings, setSettings] = useState<AppSettings | null>(null);
  const [isSaving, setIsSaving] = useState(false);
  const [statusesToClear, setStatusesToClear] = useState<JobStatus[]>([
    "discovered",
  ]);

  const methods = useForm<UpdateSettingsInput>({
    resolver: zodResolver(
      updateSettingsSchema,
    ) as Resolver<UpdateSettingsInput>,
    mode: "onChange",
    defaultValues: DEFAULT_FORM_VALUES,
  });

  const {
    handleSubmit,
    reset,
    setError,
    formState: { isDirty, errors, isValid, dirtyFields },
  } = methods;
  const settingsQuery = useQuery({
    queryKey: queryKeys.settings.current(),
    queryFn: api.getSettings,
  });
  const updateSettingsMutation = useUpdateSettingsMutation();
  const isLoading = settingsQuery.isLoading;

  useEffect(() => {
    if (!settingsQuery.data) return;
    setSettings(settingsQuery.data);
    reset(mapSettingsToForm(settingsQuery.data));
  }, [settingsQuery.data, reset]);

  useQueryErrorToast(settingsQuery.error, "Failed to load settings");

  const derived = getDerivedSettings(settings);
  const {
    model,
    pipelineWebhook,
    jobCompleteWebhook,
    display,
    envSettings,
    defaultResumeProjects,
    backup,
    scoring,
    jobSearchProfile,
    jobSources,
  } = derived;

  const canSave = isDirty && isValid;

  const onSave = async (data: UpdateSettingsInput) => {
    if (!settings) return;
    if (data.enableBasicAuth && !settings.basicAuthActive) {
      const password = data.basicAuthPassword?.trim() ?? "";
      if (!password) {
        setError("basicAuthPassword", {
          type: "manual",
          message: "Password is required when basic auth is enabled",
        });
        return;
      }
    }
    try {
      setIsSaving(true);

      // Prepare payload: nullify if equal to default
      const resumeProjectsData = data.resumeProjects;
      const resumeProjectsOverride =
        resumeProjectsData &&
        defaultResumeProjects &&
        resumeProjectsEqual(resumeProjectsData, defaultResumeProjects)
          ? null
          : resumeProjectsData;

      const envPayload: Partial<UpdateSettingsInput> = {};

      if (dirtyFields.localResumeProfilePath) {
        envPayload.localResumeProfilePath = normalizeString(
          data.localResumeProfilePath,
        );
      }

      if (dirtyFields.ukvisajobsEmail || dirtyFields.ukvisajobsPassword) {
        envPayload.ukvisajobsEmail = normalizeString(data.ukvisajobsEmail);
      }

      if (dirtyFields.adzunaAppId || dirtyFields.adzunaAppKey) {
        envPayload.adzunaAppId = normalizeString(data.adzunaAppId);
      }

      if (data.enableBasicAuth === false) {
        envPayload.basicAuthUser = null;
        envPayload.basicAuthPassword = null;
      } else if (
        dirtyFields.enableBasicAuth ||
        dirtyFields.basicAuthUser ||
        dirtyFields.basicAuthPassword
      ) {
        // If enabling basic auth or changing either field, ensure we send at least the username
        // to keep the pair consistent in the backend.
        envPayload.basicAuthUser = normalizeString(data.basicAuthUser);

        if (dirtyFields.basicAuthPassword) {
          const value = normalizePrivateInput(data.basicAuthPassword);
          if (value !== undefined) envPayload.basicAuthPassword = value;
        }
      }

      if (dirtyFields.llmProvider) {
        envPayload.llmProvider = data.llmProvider ?? null;
      }

      if (dirtyFields.llmBaseUrl) {
        envPayload.llmBaseUrl = normalizeString(data.llmBaseUrl);
      }

      if (dirtyFields.llmApiKey) {
        const value = normalizePrivateInput(data.llmApiKey);
        if (value !== undefined) envPayload.llmApiKey = value;
      }

      if (dirtyFields.ukvisajobsPassword) {
        const value = normalizePrivateInput(data.ukvisajobsPassword);
        if (value !== undefined) envPayload.ukvisajobsPassword = value;
      }

      if (dirtyFields.adzunaAppKey) {
        const value = normalizePrivateInput(data.adzunaAppKey);
        if (value !== undefined) envPayload.adzunaAppKey = value;
      }

      if (dirtyFields.webhookSecret) {
        const value = normalizePrivateInput(data.webhookSecret);
        if (value !== undefined) envPayload.webhookSecret = value;
      }

      const jobSearchProfilePayload = dirtyFields.jobSearchProfile
        ? data.jobSearchProfile
        : undefined;

      const payload: Partial<UpdateSettingsInput> = {
        ...(jobSearchProfilePayload !== undefined
          ? { jobSearchProfile: jobSearchProfilePayload }
          : {}),
        model: dirtyFields.llmProvider
          ? dirtyFields.model
            ? normalizeString(data.model)
            : null
          : normalizeString(data.model),
        modelScorer: dirtyFields.llmProvider
          ? dirtyFields.modelScorer
            ? normalizeString(data.modelScorer)
            : null
          : normalizeString(data.modelScorer),
        modelTailoring: dirtyFields.llmProvider
          ? dirtyFields.modelTailoring
            ? normalizeString(data.modelTailoring)
            : null
          : normalizeString(data.modelTailoring),
        modelProjectSelection: dirtyFields.llmProvider
          ? dirtyFields.modelProjectSelection
            ? normalizeString(data.modelProjectSelection)
            : null
          : normalizeString(data.modelProjectSelection),
        pipelineWebhookUrl: normalizeString(data.pipelineWebhookUrl),
        jobCompleteWebhookUrl: normalizeString(data.jobCompleteWebhookUrl),
        resumeProjects: resumeProjectsOverride,
        showSponsorInfo: nullIfSame(
          data.showSponsorInfo,
          display.showSponsorInfo.default,
        ),
        renderMarkdownInJobDescriptions: nullIfSame(
          data.renderMarkdownInJobDescriptions,
          display.renderMarkdownInJobDescriptions.default,
        ),
        chatStyleTone: normalizeString(data.chatStyleTone),
        chatStyleFormality: normalizeString(data.chatStyleFormality),
        chatStyleConstraints: normalizeString(data.chatStyleConstraints),
        chatStyleDoNotUse: normalizeString(data.chatStyleDoNotUse),
        chatStyleLanguageMode: data.chatStyleLanguageMode ?? null,
        chatStyleManualLanguage: data.chatStyleManualLanguage ?? null,
        backupEnabled: nullIfSame(
          data.backupEnabled,
          backup.backupEnabled.default,
        ),
        backupHour: nullIfSame(data.backupHour, backup.backupHour.default),
        backupMaxCount: nullIfSame(
          data.backupMaxCount,
          backup.backupMaxCount.default,
        ),
        penalizeMissingSalary: nullIfSame(
          data.penalizeMissingSalary,
          scoring.penalizeMissingSalary.default,
        ),
        missingSalaryPenalty: nullIfSame(
          data.missingSalaryPenalty,
          scoring.missingSalaryPenalty.default,
        ),
        blockedCompanyKeywords: (() => {
          const normalized = normalizeStringArray(data.blockedCompanyKeywords);
          const normalizedDefault = normalizeStringArray(
            scoring.blockedCompanyKeywords.default,
          );
          return stringArraysEqual(normalized, normalizedDefault)
            ? null
            : normalized;
        })(),
        blockedCountries: (() => {
          const normalized = normalizeBlockedCountryTokens(
            normalizeStringArray(data.blockedCountries),
          );
          const normalizedDefault = normalizeBlockedCountryTokens(
            scoring.blockedCountries.default,
          );
          return stringArraysEqual(normalized, normalizedDefault)
            ? null
            : normalized;
        })(),
        scoringInstructions: nullIfSame(
          normalizeString(data.scoringInstructions),
          scoring.scoringInstructions.default,
        ),
        ashbyCompanies: (() => {
          const normalized = normalizeStringArray(data.ashbyCompanies);
          return stringArraysEqual(
            normalized,
            jobSources.ashbyCompanies.default,
          )
            ? null
            : normalized;
        })(),
        greenhouseCompanies: (() => {
          const normalized = normalizeStringArray(data.greenhouseCompanies);
          return stringArraysEqual(
            normalized,
            jobSources.greenhouseCompanies.default,
          )
            ? null
            : normalized;
        })(),
        leverCompanies: (() => {
          const normalized = normalizeStringArray(data.leverCompanies);
          return stringArraysEqual(
            normalized,
            jobSources.leverCompanies.default,
          )
            ? null
            : normalized;
        })(),
        smartrecruitersCompanies: (() => {
          const normalized = normalizeStringArray(
            data.smartrecruitersCompanies,
          );
          return stringArraysEqual(
            normalized,
            jobSources.smartrecruitersCompanies.default,
          )
            ? null
            : normalized;
        })(),
        teamtailorCompanies: (() => {
          const normalized = normalizeStringArray(data.teamtailorCompanies);
          return stringArraysEqual(
            normalized,
            jobSources.teamtailorCompanies.default,
          )
            ? null
            : normalized;
        })(),
        huntflowTenants: (() => {
          const normalized = normalizeStringArray(data.huntflowTenants);
          return stringArraysEqual(
            normalized,
            jobSources.huntflowTenants.default,
          )
            ? null
            : normalized;
        })(),
        factorialTenants: (() => {
          const normalized = normalizeStringArray(data.factorialTenants);
          return stringArraysEqual(
            normalized,
            jobSources.factorialTenants.default,
          )
            ? null
            : normalized;
        })(),
        workdayTenants: (() => {
          const normalized = normalizeStringArray(data.workdayTenants);
          return stringArraysEqual(
            normalized,
            jobSources.workdayTenants.default,
          )
            ? null
            : normalized;
        })(),
        careersPageUrls: (() => {
          const normalized = normalizeStringArray(data.careersPageUrls);
          return stringArraysEqual(
            normalized,
            jobSources.careersPageUrls.default,
          )
            ? null
            : normalized;
        })(),
        icimsTenants: (() => {
          const normalized = normalizeStringArray(data.icimsTenants);
          return stringArraysEqual(normalized, jobSources.icimsTenants.default)
            ? null
            : normalized;
        })(),
        elutaRssLocations: (() => {
          const normalized = normalizeStringArray(data.elutaRssLocations);
          return stringArraysEqual(
            normalized,
            jobSources.elutaRssLocations.default,
          )
            ? null
            : normalized;
        })(),
        ...envPayload,
      };

      const updated = await updateSettingsMutation.mutateAsync(payload);

      if (
        dirtyFields.jobSearchProfile &&
        data.jobSearchProfile &&
        updated.activeProfileId
      ) {
        try {
          await api.updateProfile(updated.activeProfileId, {
            data: data.jobSearchProfile,
          });
        } catch {
          // Profile sync is best-effort; settings are already saved
        }
      }

      setSettings(updated);
      reset(mapSettingsToForm(updated));
      toast.success("Settings saved");
    } catch (error) {
      const message =
        error instanceof Error ? error.message : "Failed to save settings";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const handleClearDatabase = async () => {
    try {
      setIsSaving(true);
      const result = await api.clearDatabase();
      toast.success("Database cleared", {
        description: `Deleted ${result.jobsDeleted} jobs.`,
      });
    } catch (error) {
      const message =
        error instanceof Error ? error.message : "Failed to clear database";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const handleClearByStatuses = async () => {
    if (statusesToClear.length === 0) {
      toast.error("No statuses selected");
      return;
    }
    try {
      setIsSaving(true);
      let totalDeleted = 0;
      const results: string[] = [];

      for (const status of statusesToClear) {
        const result = await api.deleteJobsByStatus(status);
        totalDeleted += result.count;
        if (result.count > 0) {
          results.push(`${result.count} ${status}`);
        }
      }

      if (totalDeleted > 0) {
        toast.success("Jobs cleared", {
          description: `Deleted ${totalDeleted} jobs: ${results.join(", ")}`,
        });
      } else {
        toast.info("No jobs found", {
          description: `No jobs with selected statuses found`,
        });
      }
    } catch (error) {
      const message =
        error instanceof Error ? error.message : "Failed to clear jobs";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const handleClearByScore = async (threshold: number) => {
    try {
      setIsSaving(true);
      const result = await api.deleteJobsBelowScore(threshold);

      if (result.count > 0) {
        toast.success("Jobs cleared", {
          description: `Deleted ${result.count} jobs with score below ${threshold}. Applied jobs were preserved.`,
        });
      } else {
        toast.info("No jobs found", {
          description: `No jobs with score below ${threshold} found`,
        });
      }
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : "Failed to clear jobs by score";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const handleClearDiscoveredMatchingArchived = async () => {
    try {
      setIsSaving(true);
      const result = await api.deleteDiscoveredMatchingArchived();

      if (result.count > 0) {
        toast.success("Rediscovered matches cleared", {
          description: `Deleted ${result.count} discovered job${result.count === 1 ? "" : "s"} matching skipped or applied roles.`,
        });
      } else {
        toast.info("No matches found", {
          description:
            "No discovered jobs matched skipped or applied archive roles.",
        });
      }
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : "Failed to clear rediscovered archive matches";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const handleExpireDeadListings = async () => {
    try {
      setIsSaving(true);
      const result = await api.expireDeadListings();

      if (result.count > 0) {
        toast.success("Expired listings marked", {
          description: `Marked ${result.count} discovered job${result.count === 1 ? "" : "s"} as expired.`,
        });
      } else {
        toast.info("No expired listings found", {
          description: "All probed discovered job links still look live.",
        });
      }
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : "Failed to mark expired listings";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  const toggleStatusToClear = (status: JobStatus) => {
    setStatusesToClear((prev) =>
      prev.includes(status)
        ? prev.filter((s) => s !== status)
        : [...prev, status],
    );
  };
  const handleReset = async () => {
    try {
      setIsSaving(true);
      const updated = await updateSettingsMutation.mutateAsync(
        NULL_SETTINGS_PAYLOAD,
      );
      setSettings(updated);
      reset(mapSettingsToForm(updated));
      toast.success("Reset to default");
    } catch (error) {
      const message =
        error instanceof Error ? error.message : "Failed to reset settings";
      toast.error(message);
    } finally {
      setIsSaving(false);
    }
  };

  return (
    <FormProvider {...methods}>
      <PageHeader
        icon={Settings}
        title="Settings"
        subtitle="Configure runtime behavior for this app."
      />

      <main className="container mx-auto max-w-3xl space-y-6 px-4 py-6 pb-12">
        <Accordion type="multiple" className="w-full space-y-4">
          <ModelSettingsSection
            values={model}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <ScoringSettingsSection
            values={scoring}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <JobSearchProfileSection
            values={jobSearchProfile}
            activeProfileId={settings?.activeProfileId ?? null}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <KeywordSetsSettingsSection
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <JobSourcesSettingsSection
            values={jobSources}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <WebhooksSection
            pipelineWebhook={pipelineWebhook}
            jobCompleteWebhook={jobCompleteWebhook}
            webhookSecretHint={envSettings.private.webhookSecretHint}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <EnvironmentSettingsSection
            values={envSettings}
            isLoading={isLoading}
            isSaving={isSaving}
          />
          <DangerZoneSection
            statusesToClear={statusesToClear}
            toggleStatusToClear={toggleStatusToClear}
            handleClearByStatuses={handleClearByStatuses}
            handleClearDatabase={handleClearDatabase}
            handleClearByScore={handleClearByScore}
            handleClearDiscoveredMatchingArchived={
              handleClearDiscoveredMatchingArchived
            }
            handleExpireDeadListings={handleExpireDeadListings}
            isLoading={isLoading}
            isSaving={isSaving}
          />
        </Accordion>

        <div className="flex flex-wrap gap-2">
          <Button
            onClick={handleSubmit(onSave)}
            disabled={isLoading || isSaving || !canSave}
          >
            {isSaving ? "Saving..." : "Save"}
          </Button>
          <Button
            variant="outline"
            onClick={handleReset}
            disabled={isLoading || isSaving || !settings}
          >
            Reset to default
          </Button>
        </div>
        {Object.keys(errors).length > 0 && (
          <div className="text-destructive text-sm mt-2">
            Please fix the errors before saving.
          </div>
        )}
      </main>
    </FormProvider>
  );
};
