import mongoose from 'mongoose';

import { MODEL_NAMES } from './_models';
import { IInventoryItemDocWithCalculation } from './InventoryItem.model';
import { EInventoryAvailabilityResults } from '../enums/inventory-availability-results.enum';
import { IPaginatorOptions } from '../interfaces/collaterals.interface';

const mongooseLeanId = require('../plugins/id.plugin');

export interface IInventoryQuery {
  conjunction: string,
  field: string,
  filter: string,
  filterValue: string | number | Date
}

export interface IInventoryFilter {
  active: boolean;
  inventoryName: string;
  advanceRate: number;
  queries: IInventoryQuery[];
  valid: boolean;
}

export interface IInventoryAvailabilitySummary {
  order: number;
  isGroup: boolean;
  inventoryName: string;
  totalQty: number;
  totalValue: number;
  advanceRate: number;
  availability: number;
}

export interface IInventoryAvailabilityResultsView {
  items: IInventoryItemDocWithCalculation[];
  totalItems: number;
  paginatorOptions?: IPaginatorOptions;
}

export type InventoryAvailabilityPaginators = {
  [key in EInventoryAvailabilityResults]: IPaginatorOptions
}

export interface IInventoryAvailabilityResults {
  [EInventoryAvailabilityResults.UNIQ_ITEMS]: IInventoryAvailabilityResultsView;
  [EInventoryAvailabilityResults.NON_UNIQ_ITEMS]: IInventoryAvailabilityResultsView;
  [EInventoryAvailabilityResults.NOT_MATCHED_ITEMS]: IInventoryAvailabilityResultsView;
}

export interface IInventoryAvailability {
  status: string;
  bbcDateId: mongoose.Types.ObjectId;
  borrowerId: mongoose.Types.ObjectId;
  options: {
    maxAdvanceRatePriority: boolean;
  };
  useManualInputs: boolean;
  filters: IInventoryFilter[];
  summary: IInventoryAvailabilitySummary[];
  results?: IInventoryAvailabilityResults;
}

export interface IInventoryAvailabilityDocument extends IInventoryAvailability, mongoose.Document {
}

type MyFunctionType = (name: IInventoryQuery) => Object;

export interface IMapMatcherObj {
  [equation: string]: MyFunctionType;
}

export const mapMatcherObj: IMapMatcherObj = {
  GT: (query) => {
    return { [query.field]: { $gt: getNormalizedValue(query.filterValue) } };
  },
  GTE: (query) => {
    return { [query.field]: { $gte: getNormalizedValue(query.filterValue) } };
  },
  LT: (query) => {
    return { [query.field]: { $lt: getNormalizedValue(query.filterValue) } };
  },
  LTE: (query) => {
    return { [query.field]: { $lte: getNormalizedValue(query.filterValue) } };
  },
  EQ: (query) => {
    return { [query.field]: { $eq: getNormalizedValue(query.filterValue) } };
  },
  NE: (query) => {
    return { [query.field]: { $ne: getNormalizedValue(query.filterValue) } };
  },
  CON: (query) => {
    return { [query.field]: { $regex: query.filterValue.toString(), $options: 'i' } };
  },
  NC: (query) => {
    return { [query.field]: { $not: { $regex: query.filterValue.toString(), $options: 'i' } } };
  },
};

export const mapMatcherObjNew = (queries: IInventoryQuery[]) => {
  return queries.reduce((acc, query) => [...acc, mapMatcherObj[query.filter](query)], []);
};

export const getNormalizedValue = (value: string | number | Date) => {
  if (typeof value === 'number') {
    return value;
  }
  if (value instanceof Date) {
    return value;
  }
  if (value.match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/)) {
    return new Date(value.slice(0, -1));
  }
  return value;
};

export const InventoryAvailabilityScheme = new mongoose.Schema(
  {
    status: {
      type: String,
      required: true,
      default: 'NEW',
    },
    borrowerId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: MODEL_NAMES.borrowers,
      required: true,
    },
    bbcDateId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: MODEL_NAMES.inventories,
      required: true,
    },
    options: {
      maxAdvanceRatePriority: {
        type: Boolean,
        required: true,
      },
    },
    useManualInputs: {
      type: Boolean,
      default: false,
    },
    filters: [
      {
        active: {
          type: Boolean,
          required: true,
          default: true,
        },
        inventoryName: {
          type: String,
          required: true,
          trim: true,
        },
        advanceRate: {
          type: Number,
          required: true,
        },
        valid: {
          type: Boolean,
          required: true,
        },
        queries: [
          {
            conjunction: {
              type: String,
              required: true,
              trim: true,
            },
            field: {
              type: String,
              required: true,
              trim: true,
            },
            filter: {
              type: String,
              required: true,
              trim: true,
            },
            filterValue: {
              type: mongoose.Schema.Types.Mixed,
              required: true,
              trim: true,
            },
          }
        ],
      },
    ],
    summary: [
      {
        order: {
          type: Number,
          required: true,
        },
        isGroup: {
          type: Boolean,
          required: true,
        },
        inventoryName: {
          type: String,
          required: true,
        },
        totalQty: {
          type: Number,
          required: true,
        },
        totalValue: {
          type: Number,
          required: true,
        },
        advanceRate: {
          type: Number,
          required: true,
        },
        availability: {
          type: Number,
          required: true,
        },
      }
    ],
    __v: { type: Number, select: false },
  },
  { timestamps: true }
);

InventoryAvailabilityScheme.plugin(mongooseLeanId);

export const InventoryAvailabilityModel: mongoose.Model<IInventoryAvailabilityDocument> = mongoose.model<IInventoryAvailabilityDocument>(MODEL_NAMES.inventoryAvailability, InventoryAvailabilityScheme);
