export interface LogicalDumpConfig {
  enabled: boolean;
  database: string;
  scriptPath: string;
  serviceName: string;
  timerName: string;
  schedule: string;
  retentionDays: number;
  warningAfterHours: number;
  destination: { type: string; path: string };
  pgDump: { host: string; format: "custom" };
  encryption: { enabled: boolean };
}

export interface LogicalDumpRuntimeFact {
  database: string;
  databaseExists: boolean;
  scriptPath: string;
  scriptExists: boolean;
  serviceName: string;
  serviceResult: string | null;
  serviceExitStatus: number | null;
  lastSuccessAt: string | null;
  timerName: string;
  timerActive: boolean;
  timerEnabled: boolean;
  nextRunAt: string | null;
  latestNonEmptyDump: {
    path: string;
    bytes: number;
    modifiedAt: string;
  } | null;
}

export interface LogicalDumpProjection extends LogicalDumpConfig {
  observed: LogicalDumpRuntimeFact | null;
  recoverability: {
    recoverable: boolean;
    latestNonEmptyDump: LogicalDumpRuntimeFact["latestNonEmptyDump"];
  };
  warnings: string[];
}

export function projectLogicalDumps(
  declarations: LogicalDumpConfig[],
  facts: LogicalDumpRuntimeFact[] | null,
  observedAt = Date.now(),
): LogicalDumpProjection[] {
  return declarations.map((declaration) => {
    const observed = facts?.find((item) => item.database === declaration.database && item.timerName === declaration.timerName) ?? null;
    const warnings: string[] = [];
    if (!declaration.enabled) return projection(declaration, observed, warnings);
    if (observed === null) {
      warnings.push("backup-observation-unavailable");
      return projection(declaration, observed, warnings);
    }
    if (!observed.databaseExists) warnings.push("backup-database-missing");
    if (!observed.scriptExists) warnings.push("backup-script-missing");
    if (!observed.timerEnabled) warnings.push("backup-timer-disabled");
    if (!observed.timerActive) warnings.push("backup-timer-inactive");
    if (observed.lastSuccessAt === null) warnings.push("backup-recent-success-missing");
    if (observed.latestNonEmptyDump === null) {
      warnings.push("backup-latest-non-empty-dump-missing");
    } else {
      const modifiedAt = Date.parse(observed.latestNonEmptyDump.modifiedAt);
      if (!Number.isFinite(modifiedAt) || observedAt - modifiedAt > declaration.warningAfterHours * 60 * 60 * 1000) {
        warnings.push("backup-latest-dump-stale");
      }
    }
    if (observed.serviceResult !== null && observed.serviceResult !== "success") warnings.push("backup-last-run-failed");
    if (observed.serviceExitStatus !== null && observed.serviceExitStatus !== 0) warnings.push("backup-last-run-nonzero");
    return projection(declaration, observed, warnings);
  });
}

function projection(
  declaration: LogicalDumpConfig,
  observed: LogicalDumpRuntimeFact | null,
  warnings: string[],
): LogicalDumpProjection {
  return {
    ...declaration,
    observed,
    recoverability: {
      recoverable: observed?.databaseExists === true && observed.latestNonEmptyDump !== null,
      latestNonEmptyDump: observed?.latestNonEmptyDump ?? null,
    },
    warnings,
  };
}
