/**
 * Settings section: Scanning (AutoScan, Watcher, RtHydrate, RatingTagsSync).
 */

import { APP_DEFAULTS } from "../config.js";
import {
    toggleWatcher,
    getWatcherSettings,
    updateWatcherSettings,
    setExecutionGroupingSettings,
    getJxlSettings,
    setJxlSettings,
} from "../../api/client.js";
import { comfyToast } from "../toast.js";
import { t } from "../i18n.js";
import { _safeNum } from "./settingsUtils.js";
import { saveMajoorSettings, applySettingsToConfig } from "./settingsCore.js";

const SETTINGS_PREFIX = "Majoor";
const SETTINGS_CATEGORY = "Majoor Assets Manager";

/**
 * Register all Scanning-related settings.
 *
 * @param {Function} safeAddSetting - Wrapped addSetting function from the parent.
 * @param {object}   settings       - Live settings object (mutated on change).
 * @param {Function} notifyApplied  - Callback(key) to schedule change notifications.
 */
export function registerScanningSettings(safeAddSetting: (def: any) => void, settings: Record<string, any>, notifyApplied: (..._args: any[]) => void): void {
    const cat = (section: any, label: any) => [SETTINGS_CATEGORY, section, label];

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.ExecutionGrouping.Enabled`,
        category: cat(t("cat.scanning"), "Execution grouping"),
        name: "Execution job/stack grouping",
        tooltip:
            "Enable or disable all live job_id / stack_id tracking, grouping, and stack finalization logic.",
        type: "boolean",
        defaultValue: !!(
            settings.executionGrouping?.enabled ?? APP_DEFAULTS.EXECUTION_GROUPING_ENABLED
        ),
        onChange: async (value: any) => {
            const previous = !!(
                settings.executionGrouping?.enabled ?? APP_DEFAULTS.EXECUTION_GROUPING_ENABLED
            );
            const normalized = !!value;
            settings.executionGrouping = settings.executionGrouping || {};
            settings.executionGrouping.enabled = normalized;
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("executionGrouping.enabled");
            try {
                const res = await setExecutionGroupingSettings(normalized);
                if (!res?.ok) {
                    throw new Error(res?.error || "Failed to update execution grouping setting");
                }
                settings.executionGrouping.enabled = !!res?.data?.prefs?.enabled;
                saveMajoorSettings(settings);
                applySettingsToConfig(settings);
                notifyApplied("executionGrouping.enabled");
            } catch (error: any) {
                settings.executionGrouping.enabled = previous;
                saveMajoorSettings(settings);
                applySettingsToConfig(settings);
                notifyApplied("executionGrouping.enabled");
                comfyToast(
                    error?.message || "Failed to update execution grouping setting",
                    "error",
                );
            }
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.AutoScan.OnStartup`,
        category: cat(t("cat.scanning"), t("setting.scan.startup.name").replace("Majoor: ", "")),
        name: t("setting.scan.startup.name"),
        tooltip: t("setting.scan.startup.desc"),
        type: "boolean",
        defaultValue: !!settings.autoScan?.onStartup,
        onChange: (value: any) => {
            settings.autoScan = settings.autoScan || {};
            settings.autoScan.onStartup = !!value;
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("autoScan.onStartup");
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Scan.FastMode`,
        category: cat(t("cat.scanning"), "Scan mode"),
        name: "Fast scan mode",
        tooltip:
            "Use fast scan mode for manual backfill scans (skip heavier metadata work during scan).",
        type: "boolean",
        defaultValue: !!(settings.scan?.fastMode ?? true),
        onChange: (value: any) => {
            settings.scan = settings.scan || {};
            settings.scan.fastMode = !!value;
            saveMajoorSettings(settings);
            notifyApplied("scan.fastMode");
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Scan.JpegXL`,
        category: cat(t("cat.scanning"), "Image formats"),
        name: "JPEG XL (JXL) support (Experimental)",
        tooltip:
            "Index and preview .jxl images. Preview generation requires JPEG XL support in Pillow or FFmpeg. Run a new scan after enabling.",
        type: "boolean",
        defaultValue: !!settings.scan?.jxlEnabled,
        onChange: async (value: any) => {
            const previous = !!settings.scan?.jxlEnabled;
            const enabled = !!value;
            settings.scan = settings.scan || {};
            settings.scan.jxlEnabled = enabled;
            saveMajoorSettings(settings);
            notifyApplied("scan.jxlEnabled");
            try {
                const res = await setJxlSettings(enabled);
                if (!res?.ok) throw new Error(res?.error || "Failed to update JPEG XL support");
            } catch (error: any) {
                settings.scan.jxlEnabled = previous;
                saveMajoorSettings(settings);
                notifyApplied("scan.jxlEnabled");
                comfyToast(error?.message || "Failed to update JPEG XL support", "error");
            }
        },
    });

    getJxlSettings()
        .then((res) => {
            if (!res?.ok) return;
            settings.scan = settings.scan || {};
            settings.scan.jxlEnabled = !!res?.data?.prefs?.enabled;
            saveMajoorSettings(settings);
            notifyApplied("scan.jxlEnabled");
        })
        .catch(() => {});

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.RtHydrate.Concurrency`,
        category: cat(t("cat.scanning"), "Hydration"),
        name: "Hydrate Concurrency",
        tooltip: "Maximum concurrent hydration requests for rating/tags.",
        type: "number",
        defaultValue: Number(
            settings.rtHydrate?.concurrency || APP_DEFAULTS.RT_HYDRATE_CONCURRENCY || 5,
        ),
        attrs: { min: 1, max: 20, step: 1 },
        onChange: (value: any) => {
            settings.rtHydrate = settings.rtHydrate || {};
            settings.rtHydrate.concurrency = Math.max(
                1,
                Math.min(20, Math.round(_safeNum(value, APP_DEFAULTS.RT_HYDRATE_CONCURRENCY || 5))),
            );
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("rtHydrate.concurrency");
        },
    });

    const _clampWatcherValue = (raw: any, fallback: any, min: any, max: any) => {
        const parsed = Math.round(_safeNum(raw, fallback));
        return Math.max(min, Math.min(max, parsed));
    };

    const applyWatcherSettingsFromBackend = (payload: Record<string, any> = {}) => {
        const changed = [];
        settings.watcher = settings.watcher || {};
        if (typeof payload.debounce_ms === "number") {
            const normalized = Math.max(50, Math.min(5000, Math.round(payload.debounce_ms)));
            if (settings.watcher.debounceMs !== normalized) {
                settings.watcher.debounceMs = normalized;
                changed.push("watcher.debounceMs");
            }
        }
        if (typeof payload.dedupe_ttl_ms === "number") {
            const normalized = Math.max(100, Math.min(30000, Math.round(payload.dedupe_ttl_ms)));
            if (settings.watcher.dedupeTtlMs !== normalized) {
                settings.watcher.dedupeTtlMs = normalized;
                changed.push("watcher.dedupeTtlMs");
            }
        }
        if (!changed.length) {
            return;
        }
        saveMajoorSettings(settings);
        changed.forEach((key) => notifyApplied(key));
    };

    const syncWatcherRuntimeSettings = async () => {
        try {
            const res = await getWatcherSettings();
            if (!res?.ok) {
                return;
            }
            applyWatcherSettingsFromBackend(res.data || {});
        } catch (e) {
            console.debug?.(e);
        }
    };

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.Enabled`,
        category: cat(t("cat.scanning"), t("setting.watcher.enabled.label", "Enable watcher")),
        name: t("setting.watcher.name"),
        tooltip: t("setting.watcher.desc") + " (env: MJR_ENABLE_WATCHER)",
        type: "boolean",
        defaultValue: !!settings.watcher?.enabled,
        onChange: async (value: any) => {
            settings.watcher = settings.watcher || {};
            settings.watcher.enabled = !!value;
            saveMajoorSettings(settings);
            notifyApplied("watcher.enabled");
            try {
                const res = await toggleWatcher(!!value);
                if (!res?.ok) {
                    settings.watcher.enabled = !value;
                    saveMajoorSettings(settings);
                    notifyApplied("watcher.enabled");
                    comfyToast(
                        res?.error || t("toast.failedToggleWatcher", "Failed to toggle watcher"),
                        "error",
                    );
                }
            } catch {
                settings.watcher.enabled = !value;
                saveMajoorSettings(settings);
                notifyApplied("watcher.enabled");
            }
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.DebounceDelay`,
        category: cat(
            t("cat.scanning"),
            t("setting.watcher.debounce.label", "Watcher debounce delay"),
        ),
        name: t("setting.watcher.debounce.name"),
        tooltip: t("setting.watcher.debounce.desc") + " (env: MJR_WATCHER_DEBOUNCE_MS)",
        type: "number",
        defaultValue: settings.watcher?.debounceMs ?? APP_DEFAULTS.WATCHER_DEBOUNCE_MS,
        attrs: { min: 50, max: 60000, step: 50 },
        onChange: async (value: any) => {
            const fallback = APP_DEFAULTS.WATCHER_DEBOUNCE_MS;
            const clamped = _clampWatcherValue(value, fallback, 50, 60000);
            const previous = settings.watcher?.debounceMs ?? fallback;
            if (clamped === previous) return;
            settings.watcher = settings.watcher || {};
            settings.watcher.debounceMs = clamped;
            saveMajoorSettings(settings);
            try {
                const res = await updateWatcherSettings({ debounce_ms: clamped });
                if (!res?.ok) {
                    throw new Error(res?.error || t("setting.watcher.debounce.error"));
                }
                const backendValue = Math.round(Number(res?.data?.debounce_ms ?? clamped));
                settings.watcher.debounceMs = backendValue;
                saveMajoorSettings(settings);
                notifyApplied("watcher.debounceMs");
            } catch (error: any) {
                settings.watcher.debounceMs = previous;
                saveMajoorSettings(settings);
                notifyApplied("watcher.debounceMs");
                comfyToast(error?.message || t("setting.watcher.debounce.error"), "error");
            }
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.DedupeWindow`,
        category: cat(
            t("cat.scanning"),
            t("setting.watcher.dedupe.label", "Watcher dedupe window"),
        ),
        name: t("setting.watcher.dedupe.name"),
        tooltip: t("setting.watcher.dedupe.desc") + " (env: MJR_WATCHER_DEDUPE_TTL_MS)",
        type: "number",
        defaultValue: settings.watcher?.dedupeTtlMs ?? APP_DEFAULTS.WATCHER_DEDUPE_TTL_MS,
        attrs: { min: 100, max: 120000, step: 100 },
        onChange: async (value: any) => {
            const fallback = APP_DEFAULTS.WATCHER_DEDUPE_TTL_MS;
            const clamped = _clampWatcherValue(value, fallback, 100, 120000);
            const previous = settings.watcher?.dedupeTtlMs ?? fallback;
            if (clamped === previous) return;
            settings.watcher = settings.watcher || {};
            settings.watcher.dedupeTtlMs = clamped;
            saveMajoorSettings(settings);
            try {
                const res = await updateWatcherSettings({ dedupe_ttl_ms: clamped });
                if (!res?.ok) {
                    throw new Error(res?.error || t("setting.watcher.dedupe.error"));
                }
                const backendValue = Math.round(Number(res?.data?.dedupe_ttl_ms ?? clamped));
                settings.watcher.dedupeTtlMs = backendValue;
                saveMajoorSettings(settings);
                notifyApplied("watcher.dedupeTtlMs");
            } catch (error: any) {
                settings.watcher.dedupeTtlMs = previous;
                saveMajoorSettings(settings);
                notifyApplied("watcher.dedupeTtlMs");
                comfyToast(error?.message || t("setting.watcher.dedupe.error"), "error");
            }
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.MaxPending`,
        category: cat(t("cat.scanning"), "Watcher queue"),
        name: "Watcher: max pending files",
        tooltip: "Maximum number of pending watcher files kept in memory.",
        type: "number",
        defaultValue: Number(settings.watcher?.maxPending ?? 500),
        attrs: { min: 10, max: 5000, step: 10 },
        onChange: (value: any) => {
            settings.watcher = settings.watcher || {};
            settings.watcher.maxPending = Math.max(
                10,
                Math.min(5000, Math.round(_safeNum(value, 500))),
            );
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("watcher.maxPending");
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.MinSize`,
        category: cat(t("cat.scanning"), "Watcher file size"),
        name: "Watcher: min size (bytes)",
        tooltip: "Minimum file size indexed by watcher.",
        type: "number",
        defaultValue: Number(settings.watcher?.minSize ?? 100),
        attrs: { min: 0, max: 1000000, step: 100 },
        onChange: (value: any) => {
            settings.watcher = settings.watcher || {};
            settings.watcher.minSize = Math.max(
                0,
                Math.min(1000000, Math.round(_safeNum(value, 100))),
            );
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("watcher.minSize");
        },
    });

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.Watcher.MaxSize`,
        category: cat(t("cat.scanning"), "Watcher file size"),
        name: "Watcher: max size (bytes)",
        tooltip: "Maximum file size indexed by watcher.",
        type: "number",
        defaultValue: Number(settings.watcher?.maxSize ?? 4294967296),
        attrs: { min: 100000, max: 17179869184, step: 100000 },
        onChange: (value: any) => {
            settings.watcher = settings.watcher || {};
            settings.watcher.maxSize = Math.max(
                100000,
                Math.min(17179869184, Math.round(_safeNum(value, 4294967296))),
            );
            saveMajoorSettings(settings);
            applySettingsToConfig(settings);
            notifyApplied("watcher.maxSize");
        },
    });

    try {
        syncWatcherRuntimeSettings().catch(() => {});
    } catch (e) {
        console.debug?.(e);
    }

    safeAddSetting({
        id: `${SETTINGS_PREFIX}.RatingTagsSync.Enabled`,
        category: cat(t("cat.scanning"), t("setting.sync.rating.name").replace("Majoor: ", "")),
        name: t("setting.sync.rating.name"),
        tooltip: t("setting.sync.rating.desc"),
        type: "boolean",
        defaultValue: !!settings.ratingTagsSync?.enabled,
        onChange: (value: any) => {
            settings.ratingTagsSync = settings.ratingTagsSync || {};
            settings.ratingTagsSync.enabled = !!value;
            saveMajoorSettings(settings);
            notifyApplied("ratingTagsSync.enabled");
        },
    });
}
