/**
 * @file folderField.ts - API wrapper for IWS5 folder field objects and functions.
 */

import type { FieldInstanceType } from '../types/fieldInstance.ts';
import { assembleQueryParams, FetchError } from '../utils/fetchHelper.ts';
import { getAccessToken } from '../services/authService.ts';
import { StatusCodes } from 'http-status-codes';

/** Base URL for the IWS5 folder fields API endpoint */
export const FOLDER_FIELDS_API_URL =
  window.__APP_CONFIG__?.IWS5_API_URL + '/api/FolderField';

/**
 * Represents a folder field associated with a work item or process.
 * Folder fields store key-value metadata used for categorization and workflow logic.
 */
export type FolderField = {
  /** Unique identifier of the field definition */
  fieldId: number;

  /** Unique identifier of this specific field instance */
  fieldInstanceId: number;

  /** ID of the process this field belongs to; present on process-level fields */
  fieldProcessId?: number;

  /** Data type of the field value (e.g., String, Number, Date, Option) */
  dataType?: FieldInstanceType;

  /** Instance type classification for the field */
  instanceType: FieldInstanceType;

  /** Display name of the field */
  fieldName: string;

  /** Current value of the field as a string */
  fieldValue: string;

  /** Whether the current user has permission to edit this field */
  editAllowed?: boolean;

  /** Positional index for ordering the field in the UI; null if unspecified */
  index?: number | null;

  /** Optional alias used as an alternative display name */
  alias?: string | null;

  /** The original value of the field before any edits; null if unset */
  initialValue?: string | null;

  /** List of allowed values for Option-type fields; null for other types */
  fieldOptions?: string[] | null;
};

/**
 * Retrieve folder fields for a given work item.
 *
 * @param workItemReferenceId ID of work item to retrieve folder fields for
 * @returns List of work item folder fields
 * @throws FetchError If response is not 200-class
 */
export async function getWorkItemFolderFields(
  workItemReferenceId: number
): Promise<FolderField[]> {
  const _url = assembleQueryParams(FOLDER_FIELDS_API_URL, {
    workItemReferenceId,
  });

  // Attempt GET /api/FolderField
  try {
    const response = await fetch(_url.toString(), {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAccessToken()}`,
      },
    });
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(
        errorText || 'Failed to retrieve WorkItem folder fields',
        response
      );
    }
    if (response.status === StatusCodes.NO_CONTENT) {
      return [] as FolderField[];
    }

    return (await response.json()).fieldList as FolderField[];
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

/**
 * Retrieves folder fields for a given process.
 *
 * @param processId ID of process to retrieve folder fields for
 * @returns List of folder fields
 * @throws FetchError If response is not 200-class
 */
export async function getProcessFolderFields(
  processId: number
): Promise<FolderField[]> {
  const _url = FOLDER_FIELDS_API_URL + '/process/' + processId;

  // Attempt GET /api/FolderField/process/{processId}
  try {
    const response = await fetch(_url.toString(), {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAccessToken()}`,
      },
    });
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(
        errorText || 'Failed to retrieve process folder fields',
        response
      );
    }
    if (response.status === StatusCodes.NO_CONTENT) {
      return [] as FolderField[];
    }

    return (await response.json()) as FolderField[];
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

/**
 * Updates a work item's folder fields.
 *
 * @param workItemId ID of work item to update folder fields for
 * @param updatedFields Folder field objects containing new values
 * @throws FetchError If response is not 200-class
 */
export async function updateFolderFields(
  workItemId: number,
  updatedFields: FolderField[]
): Promise<void> {
  const _url = FOLDER_FIELDS_API_URL + '/update-values';

  // Attempt PUT /api/FolderField/update-values
  try {
    const response = await fetch(_url, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAccessToken()}`,
      },
      body: JSON.stringify({
        workItemId,
        updatedFields: updatedFields.map((field) => {
          const { fieldValue: newValue, ...rest } = field;
          return { newValue, ...rest };
        }),
      }),
    });
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(
        errorText || 'Failed to update folder field values',
        response
      );
    }
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}
