/**
 * Windows Task Scheduler integration for the daily watched-source sync.
 * Registers a task that launches AeroFetch with
 * `--sync` once a day; the app then runs its startup sync of watched sources.
 *
 * NOTE: this is OS-level wiring and cannot be exercised in the Vite UI preview or
 * the unit tests — like the `aerofetch://` protocol registration, it needs a real
 * install + manual smoke test. `schtasks` is invoked via execFile (no shell), and
 * the only interpolated value is the trusted `process.execPath`.
 */

import { execFile } from 'child_process'
import { getSystem32Path } from './binaries'
import { isValidSyncTime, DEFAULT_SETTINGS, type ScheduledSyncStatus } from '@shared/ipc'

const TASK_NAME = 'AeroFetchDailySync'
/** The argv flag the scheduled task passes so startup knows it's a sync launch. */
export const SYNC_FLAG = '--sync'

function schtasks(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> {
  return new Promise((resolve) => {
    // Resolve schtasks from System32 by absolute path, not the bare name, so a
    // planted schtasks.exe on PATH / in the CWD can't be invoked instead. (audit F3)
    execFile(
      getSystem32Path('schtasks.exe'),
      args,
      { windowsHide: true },
      (err, stdout, stderr) => {
        const code = err ? ((err as { code?: number }).code ?? 1) : 0
        resolve({ code, stdout: String(stdout), stderr: String(stderr) })
      }
    )
  })
}

/** True when this launch came from the scheduled task (argv carries --sync). */
export function isSyncLaunch(argv: string[]): boolean {
  return argv.includes(SYNC_FLAG)
}

/** Whether the daily-sync scheduled task is currently registered. */
export async function getScheduledSync(): Promise<ScheduledSyncStatus> {
  const r = await schtasks(['/Query', '/TN', TASK_NAME])
  return { enabled: r.code === 0 }
}

/**
 * Register or remove the daily-sync scheduled task. Creating runs AeroFetch with
 * `--sync` every day at the given 24h 'HH:MM' time (overwriting any prior task of
 * the same name — which is also how a time change re-registers, L51). The time is
 * renderer-supplied over IPC, so it's validated here before reaching the schtasks
 * argv; anything malformed falls back to the default rather than failing the toggle.
 */
export async function setScheduledSync(
  enabled: boolean,
  time?: string
): Promise<ScheduledSyncStatus> {
  if (enabled) {
    const startTime = isValidSyncTime(time) ? time : DEFAULT_SETTINGS.syncTime
    const tr = `"${process.execPath}" ${SYNC_FLAG}`
    const r = await schtasks([
      '/Create',
      '/F',
      '/SC',
      'DAILY',
      '/ST',
      startTime,
      '/TN',
      TASK_NAME,
      '/TR',
      tr
    ])
    return {
      enabled: r.code === 0,
      error: r.code === 0 ? undefined : r.stderr.trim() || 'Could not create the scheduled task.'
    }
  }
  const r = await schtasks(['/Delete', '/F', '/TN', TASK_NAME])
  // A missing task ("cannot find") is success for our purposes — it's already gone.
  const gone = r.code === 0 || /cannot find|does not exist/i.test(r.stderr)
  return { enabled: gone ? false : true, error: gone ? undefined : r.stderr.trim() }
}
