import mongoose, { Model, Schema } from 'mongoose';

import { MODEL_NAMES } from './_models';

export interface ICashAllocationProduct {
  _id?: mongoose.Types.ObjectId;
  isProduct: boolean;
  isCommonSplit: boolean;
  order: number;
  productId: mongoose.Types.ObjectId;
  nonProductName: string;
  accounts: {
    [companyId: string]: {
      accountId1: mongoose.Types.ObjectId | 'BANK';
      accountId2: mongoose.Types.ObjectId | 'BANK';
    };
  };
  class: string;
  location: string;
  DRMemo: string;
  CRMemo: string;
}

export type ICashAllocationProductStringified = {
  [K in keyof ICashAllocationProduct]: K extends '_id' | 'productId' ? string : ICashAllocationProduct[K];
};

type CashAllocationProductModel = Model<ICashAllocationProduct>;

const normalizeAccountValue = (
  value: unknown,
): mongoose.Types.ObjectId | 'BANK' | null => {
  if (value === null || value === undefined || value === '') {
    return null;
  }

  if (value === 'BANK') {
    return 'BANK';
  }

  if (typeof value === 'string' && mongoose.isValidObjectId(value)) {
    return new mongoose.Types.ObjectId(value);
  }

  if (value instanceof mongoose.Types.ObjectId) {
    return value;
  }

  return null;
};

export const AccountSchema = {
  type: Map,
  of: new Schema({
    accountId1: {
      type: Schema.Types.Mixed,
      required: false,
      set: normalizeAccountValue,
      validate: {
        validator: function(value: mongoose.Types.ObjectId | 'BANK' | null) {
          return (
            value === null ||
            mongoose.isValidObjectId(value) ||
            value === 'BANK'
          );
        },
        message: (props: any) =>
          `${props.value} is not a valid ObjectId, "BANK", or null`,
      },
    },
    accountId2: {
      type: Schema.Types.Mixed,
      required: false,
      set: normalizeAccountValue,
      validate: {
        validator: function(value: mongoose.Types.ObjectId | 'BANK' | null) {
          return (
            value === null ||
            mongoose.isValidObjectId(value) ||
            value === 'BANK'
          );
        },
        message: (props: any) =>
          `${props.value} is not a valid ObjectId, "BANK", or null`,
      },
    },
  }),
  default: {},
};

export const CashAllocationProductSchema = new mongoose.Schema<ICashAllocationProduct, CashAllocationProductModel>(
  {
    order: {
      type: Number,
      required: true,
    },
    isProduct: {
      type: Boolean,
      required: true,
    },
    isCommonSplit: {
      type: Boolean,
      required: false,
    },
    productId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: MODEL_NAMES.loanProducts,
      required: false,
    },
    nonProductName: {
      type: String,
      required: false,
      trim: true,
    },
    accounts: AccountSchema,
    class: {
      type: String,
      required: false,
      trim: true,
    },
    location: {
      type: String,
      required: false,
      trim: true,
    },
    DRMemo: {
      type: String,
      required: false,
      trim: true,
    },
    CRMemo: {
      type: String,
      required: false,
      trim: true,
    },
  },
  {
    timestamps: { createdAt: true, updatedAt: false },
  },
);

export const CashAllocationProduct = mongoose.model<ICashAllocationProduct, CashAllocationProductModel>(MODEL_NAMES.cashAllocationProducts, CashAllocationProductSchema);
