// Small, reusable value transformers for the meeting-summary DTOs.
// They make the endpoints accept both JSON requests and multipart/form-data
// requests (where every field arrives as a plain string).

/**
 * Turn a value into an array of numbers.
 * Accepts a real array, a comma-separated string ("2,3"), or a single value.
 * Returns undefined for empty/blank input so the field stays optional.
 */
export function toNumberArray(value: unknown): number[] | undefined {
  if (value === undefined || value === null || value === '') {
    return undefined;
  }

  if (Array.isArray(value)) {
    return value.map(Number).filter(Number.isFinite);
  }

  if (typeof value === 'string') {
    return value
      .split(',')
      .map((item) => Number(item.trim()))
      .filter(Number.isFinite);
  }

  const numberValue = Number(value);

  return Number.isFinite(numberValue) ? [numberValue] : undefined;
}

/**
 * Parse the `issueResolves` field. In multipart requests it arrives as a JSON
 * string, so we parse it into an array of plain objects. When it is already an
 * array (a normal JSON request) we pass it through untouched.
 */
export function toIssueResolveArray(value: unknown): unknown {
  if (typeof value !== 'string') {
    return value;
  }

  const trimmed = value.trim();
  if (trimmed === '') {
    return undefined;
  }

  try {
    const parsed: unknown = JSON.parse(trimmed);
    return Array.isArray(parsed) ? parsed : [parsed];
  } catch {
    // Return the raw string so validation fails with a clear message.
    return value;
  }
}

/**
 * Parse a rich-text editor value. The editor sends structured JSON, which
 * arrives as a string in multipart requests. We parse it when possible and
 * otherwise keep the original value (a plain string is still valid JSON data).
 */
export function toJsonValue(value: unknown): unknown {
  if (typeof value !== 'string') {
    return value;
  }

  const trimmed = value.trim();
  if (trimmed === '') {
    return undefined;
  }

  try {
    return JSON.parse(trimmed);
  } catch {
    return value;
  }
}

/**
 * Normalize a human-friendly status label into the IssueResolveStatus enum
 * token the database expects. "In Progress" -> "IN_PROGRESS",
 * "Not Address(ed)" -> "NOT_ADDRESSED". "New Submission" means the issue has
 * no resolution yet, so we drop it (undefined) and let the default apply.
 */
export function toIssueResolveStatus(value: unknown): unknown {
  if (typeof value !== 'string') {
    return value;
  }

  const normalized = value.trim().toUpperCase().replace(/\s+/g, '_');

  if (normalized === '') {
    return undefined;
  }
  if (normalized === 'NOT_ADDRESS') {
    return 'NOT_ADDRESSED';
  }
  if (normalized === 'NEW_SUBMISSION' || normalized === 'NEW_SUBMITTED') {
    return undefined;
  }

  return normalized;
}

/**
 * Normalize the optional issue-escalation field.
 * Undefined means "do not touch existing escalation".
 * Null means "clear existing escalation".
 */
export function toIssueEscalationTarget(value: unknown): unknown {
  if (value === undefined) {
    return undefined;
  }

  if (value === null) {
    return null;
  }

  if (typeof value !== 'string') {
    return value;
  }

  const trimmed = value.trim();

  if (
    trimmed === '' ||
    trimmed.toLowerCase() === 'null' ||
    trimmed === 'Not Uploaded'
  ) {
    return null;
  }

  return trimmed.toUpperCase();
}
