import { Collection } from "@discordjs/collection";
import {
  CredentialField,
  CredentialTypeField,
  ValueType,
} from "./credentialTypeDefs";
import GenericError from "../../Errors/GenericError";

export interface ValidatedField {
  fieldId: string;
  value: string;
  secure: boolean;
  isMultiCredential?: boolean;
  subCredentials?: string[];
}

/**
 * Field Validator
 *
 * This method will take a record of the fields being submitted and compare them against a record of the acceptable fields
 * for a credential type.  If any of the submitted fields do not match an acceptable field, an error will be thrown.
 *
 * If all the credentials pass, it will return a processed version of the submitted fields including fields that need to be
 * stored securely (encrypted) and fields that do not.
 *
 * Multi-credential fields are handled specially — they don't carry a direct value but instead
 * reference sub-credential IDs.
 *
 * @param fields - The fields in object form that are being submitted.
 * @param acceptableFields - The acceptable field to be compared against.
 */
export const fieldValidator = async (
  fields: CredentialField[],
  acceptableFields: CredentialTypeField[],
): Promise<ValidatedField[]> => {
  const afCollection = new Collection(acceptableFields.map((f) => [f.id, f]));

  await Promise.all(
    fields.map(async (field) => {
      const matchingField = afCollection.get(field.fieldId);
      if (!matchingField) {
        throw new GenericError({
          message: `Invalid field ID: ${field.fieldId}`,
          name: "InvalidCredentialField",
          cause: `No acceptable field with ID '${field.fieldId}' found.`,
          status: 400,
        });
      }

      return;
    }),
  );

  return fields.map((field) => {
    const matchingField = afCollection.get(field.fieldId)!;

    // Multi-credential fields don't carry a direct value;
    // they reference sub-credential IDs instead.
    if (matchingField.valueType === ValueType.MULTI_CREDENTIAL) {
      return {
        fieldId: field.fieldId,
        value: "",
        secure: false,
        isMultiCredential: true,
        subCredentials: field.subCredentials ?? [],
      };
    }

    return {
      fieldId: field.fieldId,
      value: field.value,
      secure: matchingField.secure,
    };
  });
};
