/**
 * @file
 * Load and persist the help sync's "last synced" baseline: a map of `<resourceSlug>:<localeCode>` to
 * the Transifex last-translation-update timestamp we most recently pushed to Freshdesk. Comparing a
 * pair's current Transifex stat against this baseline is how the sync decides whether that pair needs
 * re-pushing, so only changed pairs incur Freshdesk writes.
 *
 * The file location is overridable via `HELP_SYNC_BASELINE_FILE` so the persistence mechanism (a
 * committed repo file vs. a restored CI cache) can change without touching this code.
 */
import { readFileSync, writeFileSync, mkdirSync, renameSync } from 'fs'
import { dirname } from 'path'
import { messageOf } from './errors.mts'

/** A map of `<resourceSlug>:<localeCode>` to the last-synced timestamp (or null if never translated). */
export type SyncBaseline = Map<string, string | null>

/** Default on-disk location of the baseline manifest, relative to the repository root. */
export const DEFAULT_BASELINE_PATH = 'scripts/help-sync-baseline.json'

const baselinePath = (path?: string): string => path ?? process.env.HELP_SYNC_BASELINE_FILE ?? DEFAULT_BASELINE_PATH

/**
 * Load the baseline from disk. A missing baseline means "sync everything" — the safe default — so an
 * absent or unreadable file resolves to an empty map rather than an error.
 * @param path - override for the baseline file location; defaults to `HELP_SYNC_BASELINE_FILE` or {@link DEFAULT_BASELINE_PATH}
 * @returns the persisted baseline, or an empty map if none exists yet
 */
export const loadBaseline = (path?: string): SyncBaseline => {
  const file = baselinePath(path)
  try {
    const parsed = JSON.parse(readFileSync(file, 'utf8')) as Record<string, string | null>
    return new Map(Object.entries(parsed))
  } catch (error) {
    // A corrupt or unreadable file must not wedge the run: log (unless it is simply absent) and start
    // fresh, which just means a one-time full sync that repopulates the baseline.
    if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
      console.warn(`Could not read help-sync baseline "${file}"; treating as empty: ${messageOf(error)}`)
    }
    return new Map()
  }
}

/**
 * Persist the baseline to disk, sorted by key for a stable, diff-friendly file. The write is atomic
 * (write a temp file, then rename over the target) because the sync flushes the baseline repeatedly
 * as it makes progress and relies on the file surviving an abrupt kill (a job timeout mid-write). A
 * partial `writeFileSync` would otherwise leave a truncated file that {@link loadBaseline} discards as
 * corrupt, throwing away every pair synced so far. `renameSync` within the same directory is atomic.
 * @param baseline - the baseline to write
 * @param path - override for the baseline file location; defaults to `HELP_SYNC_BASELINE_FILE` or {@link DEFAULT_BASELINE_PATH}
 */
export const saveBaseline = (baseline: SyncBaseline, path?: string): void => {
  const file = baselinePath(path)
  mkdirSync(dirname(file), { recursive: true })
  const sorted: Record<string, string | null> = {}
  for (const key of [...baseline.keys()].sort()) {
    sorted[key] = baseline.get(key) ?? null
  }
  const tempFile = `${file}.tmp`
  writeFileSync(tempFile, JSON.stringify(sorted, null, 2) + '\n')
  renameSync(tempFile, file)
}
