import type { DesktopUpdateStatus } from "../shared/update";

export type UpdateCheckTrigger = "boot" | "interval" | "manual";

export interface UpdateCheckPlan {
  readonly check: boolean;
  readonly promptVersionAfterCheck: string | null;
}

export interface CompletedUpdateCheckInput {
  readonly stagedVersion: string | null;
  readonly trigger: UpdateCheckTrigger;
  readonly updateAvailable: boolean;
  readonly availableVersion: string | null;
}

export interface DownloadedUpdateInput {
  readonly stagedVersion: string | null;
  readonly declinedVersion: string | null;
  readonly incomingVersion: string;
  readonly trigger: UpdateCheckTrigger;
}

export interface DownloadedUpdatePlan {
  readonly stagedVersion: string;
  readonly declinedVersion: string | null;
  readonly status: DesktopUpdateStatus;
  readonly promptVersion: string | null;
}

const parseVersionParts = (version: string): readonly number[] | null => {
  const core = version.trim().split(/[+-]/, 1)[0];
  if (!core) return null;
  const parts = core.split(".").map((part) => Number.parseInt(part, 10));
  return parts.every((part) => Number.isInteger(part) && part >= 0) ? parts : null;
};

export const compareUpdateVersions = (left: string, right: string): number | null => {
  const leftParts = parseVersionParts(left);
  const rightParts = parseVersionParts(right);
  if (!leftParts || !rightParts) return null;
  const length = Math.max(leftParts.length, rightParts.length);
  for (let index = 0; index < length; index += 1) {
    const l = leftParts[index] ?? 0;
    const r = rightParts[index] ?? 0;
    if (l !== r) return l > r ? 1 : -1;
  }
  return 0;
};

const isNewerUpdateVersion = (incomingVersion: string, declinedVersion: string): boolean => {
  const comparison = compareUpdateVersions(incomingVersion, declinedVersion);
  return comparison === null ? incomingVersion !== declinedVersion : comparison > 0;
};

export const planUpdateCheck = (input: {
  readonly stagedVersion: string | null;
  readonly trigger: UpdateCheckTrigger;
}): UpdateCheckPlan => ({
  check: true,
  promptVersionAfterCheck: input.trigger === "manual" ? input.stagedVersion : null,
});

export const planCompletedUpdateCheck = (
  input: CompletedUpdateCheckInput,
): { readonly promptVersion: string | null } => {
  if (input.trigger !== "manual" || !input.stagedVersion) return { promptVersion: null };
  if (!input.updateAvailable) return { promptVersion: input.stagedVersion };
  if (!input.availableVersion) return { promptVersion: null };
  const comparison = compareUpdateVersions(input.availableVersion, input.stagedVersion);
  if (comparison === null) {
    return {
      promptVersion: input.availableVersion === input.stagedVersion ? input.stagedVersion : null,
    };
  }
  return { promptVersion: comparison <= 0 ? input.stagedVersion : null };
};

export const planDownloadedUpdate = (input: DownloadedUpdateInput): DownloadedUpdatePlan => {
  const prompt =
    input.trigger === "manual" ||
    !input.declinedVersion ||
    isNewerUpdateVersion(input.incomingVersion, input.declinedVersion);
  return {
    stagedVersion: input.incomingVersion,
    declinedVersion: input.declinedVersion,
    status: { state: "downloaded", version: input.incomingVersion },
    promptVersion: prompt ? input.incomingVersion : null,
  };
};

export const statusAfterUpdateError = (
  status: DesktopUpdateStatus,
  message: string,
): DesktopUpdateStatus => {
  if (status.state === "available" || status.state === "downloading" || status.state === "error") {
    return { state: "error", version: status.version, message };
  }
  return status;
};

export const planFatalAutoInstallOnQuit = (input: {
  readonly packaged: boolean;
  readonly retryAfterReset: boolean;
}): { readonly enableDuringFailure: boolean; readonly restoreAfterRecovery: boolean } => ({
  enableDuringFailure: input.packaged,
  restoreAfterRecovery: input.packaged && input.retryAfterReset,
});
