// Pure helpers for the application promotion pipeline: producing the next
// version's deployment plan and advancing assignment rings. No I/O, so both are
// unit-tested; the controller persists/pushes the results.

// Bump the trailing numeric component of a version string (1.2.3 -> 1.2.4,
// 126 -> 127). Falls back to appending ".1" when no number is present.
export function bumpVersion(version = '') {
  const text = String(version).trim();
  if (!text) return '1.0.0';
  const match = text.match(/^(.*?)(\d+)(\D*)$/);
  if (!match) return `${text}.1`;
  const [, head, num, tail] = match;
  return `${head}${Number(num) + 1}${tail}`;
}

// Build a fresh deployment payload for the next version, based on an existing
// plan. The new plan is unpublished (no graphAppId), draft status, and keeps the
// application link so the catalog groups both versions.
export function cloneForNextVersion(deployment, { version, applicationId } = {}) {
  if (!deployment) throw new Error('A source deployment is required');
  const nextVersion = version || bumpVersion(extractVersion(deployment.name));
  const baseName = deployment.name.replace(/\s+\d[\d.]*$/, '').trim() || deployment.name;
  return {
    name: `${baseName} ${nextVersion}`.trim(),
    profileId: deployment.profileId || null,
    scriptId: deployment.scriptId || null,
    applicationId: applicationId || deployment.applicationId || null,
    sourceFolder: deployment.sourceFolder || '',
    intunewinFile: '',
    commandStyle: deployment.commandStyle,
    installBehavior: deployment.installBehavior,
    restartBehavior: deployment.restartBehavior,
    uiMode: deployment.uiMode,
    requirements: { ...(deployment.requirements || {}) },
    installCommand: deployment.installCommand,
    uninstallCommand: deployment.uninstallCommand,
    detectionType: deployment.detectionType,
    detectionRule: deployment.detectionRule,
    returnCodes: deployment.returnCodes || [],
    assignments: deployment.assignments || [],
    relationships: deployment.relationships || [],
    status: 'draft',
    graphAppId: '',
    graphConnectionId: deployment.graphConnectionId || null,
    notes: `Version ${nextVersion} created from ${deployment.name}.`,
    visibility: deployment.visibility,
    groupId: deployment.groupId || null
  };
}

function extractVersion(name = '') {
  const match = String(name).match(/(\d[\d.]*)\s*$/);
  return match ? match[1] : '';
}

// Advance a named assignment ring to a new intent (e.g. promote "Broad" from
// available to required). Returns a new assignments array; unknown rings are a
// no-op so callers can detect "nothing changed".
export function setRingIntent(assignments = [], ring, intent) {
  let changed = false;
  const next = assignments.map((assignment) => {
    if (assignment.ring?.toLowerCase() === String(ring).toLowerCase() && assignment.intent !== intent) {
      changed = true;
      return { ...assignment, intent };
    }
    return assignment;
  });
  return { assignments: next, changed };
}
