/**
 * desktop/src/settings-store.ts — B3: on-disk prefs persistence (get/set).
 *
 * A minimal, synchronous JSON store for DesktopPrefs — hand-rolled instead of
 * electron-store to match the project's minimal-deps convention. It uses ONLY
 * node:fs + node:path (no electron) so it is unit-testable against a real temp
 * dir; the caller supplies the userData directory. Reads always go through
 * validatePrefs, so a corrupt or hand-edited file degrades to defaults rather
 * than crashing the app; writes are best-effort (a failure is logged, and the
 * in-memory merged result is still returned so the session reflects the change).
 */

import fs from 'node:fs'
import path from 'node:path'
import type { Logger } from './logger.js'
import type { DesktopPrefs } from './types.js'
import { defaultPrefs, mergePrefs, validatePrefs } from './prefs.js'

/** Filename under the store dir; 2-space JSON stays human-readable/editable. */
const PREFS_FILENAME = 'prefs.json'
const JSON_INDENT = 2

export interface SettingsStore {
  get(): DesktopPrefs
  set(patch: Partial<DesktopPrefs>): DesktopPrefs
}

/** True iff `err` is a Node "file not found" error — the normal first-run case. */
function isFileNotFound(err: unknown): boolean {
  return err instanceof Error && (err as NodeJS.ErrnoException).code === 'ENOENT'
}

/**
 * Create a SettingsStore backed by `<dir>/prefs.json`.
 * - get():  read + parse + validate; missing file → defaults (silent);
 *           unreadable/corrupt file → defaults (logged warn, never throws).
 * - set():  merge the patch over current prefs, persist, return the merged prefs;
 *           a write failure is logged (error) but still returns the merged result.
 */
export function createSettingsStore(
  dir: string,
  platform: NodeJS.Platform,
  logger: Logger,
): SettingsStore {
  const filePath = path.join(dir, PREFS_FILENAME)

  function get(): DesktopPrefs {
    let contents: string
    try {
      contents = fs.readFileSync(filePath, 'utf8')
    } catch (err: unknown) {
      // A missing file is expected before the first save — don't cry wolf.
      if (!isFileNotFound(err)) {
        logger.warn(`could not read prefs at ${filePath}, using defaults`)
      }
      return defaultPrefs(platform)
    }
    try {
      const parsed: unknown = JSON.parse(contents)
      return validatePrefs(parsed, platform)
    } catch {
      // Corrupt JSON on disk (hand-edit gone wrong) — fall back, don't crash.
      logger.warn(`prefs at ${filePath} is not valid JSON, using defaults`)
      return defaultPrefs(platform)
    }
  }

  function set(patch: Partial<DesktopPrefs>): DesktopPrefs {
    const merged = mergePrefs(get(), patch)
    try {
      fs.mkdirSync(dir, { recursive: true })
      fs.writeFileSync(filePath, JSON.stringify(merged, null, JSON_INDENT), 'utf8')
    } catch (err: unknown) {
      // Best-effort persistence: report the failure but still hand back the
      // merged prefs so the settings UI reflects the user's change this session.
      logger.error(`failed to persist prefs to ${filePath}`, err)
    }
    return merged
  }

  return { get, set }
}
