import { Option } from "common/SearchSelect";
import { GrainOptions, ToGrainOption } from "grain";
import GrainDescriber from "grain/GrainDescriber";
import { cloneDeep } from "lodash";
import { pond } from "protobuf-ts/pond";
import { getGrainUnit, stringToMaterialColour } from "utils";
import { or } from "utils/types";

export class Contract {
  public settings: pond.ContractSettings = pond.ContractSettings.create();
  public title: string = "Contract";
  public colour: string = "";
  public unit: string = "";
  public label: string = "";
  private objKey: string = "";

  public static create(pb?: pond.Contract): Contract {
    let my = new Contract();
    if (pb) {
      my.settings = pond.ContractSettings.fromObject(cloneDeep(or(pb.settings, {})));
      my.title = pb.name;
      my.objKey = pb.key;
      my.unit = my.measurementUnit();
      my.colour = my.commodityColour();
      my.label = my.commodityLabel();
    }
    return my;
  }

  private measurementUnit(): string {
    if (this.settings.type === pond.ContractType.CONTRACT_TYPE_GRAIN) {
      return getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? "mT" : "bu";
    }
    return "";
  }

  private commodityColour(): string {
    if (this.settings.type === pond.ContractType.CONTRACT_TYPE_GRAIN) {
      if (this.isCustom()) {
        return stringToMaterialColour(this.settings.customCommodity);
      } else {
        return GrainDescriber(this.settings.commodity).colour;
      }
    }
    return "blue";
  }

  public commodityLabel(): string {
    let label = "";
    if (this.settings.type === pond.ContractType.CONTRACT_TYPE_GRAIN) {
      if (this.isCustom()) {
        label = this.settings.customCommodity;
      } else {
        label = GrainDescriber(this.settings.commodity).name;
      }
    }
    return label;
  }

  public static clone(other?: Contract): Contract {
    if (other) {
      return Contract.create(
        pond.Contract.fromObject({
          title: other.title,
          key: other.objKey,
          settings: cloneDeep(other.settings)
        })
      );
    }
    return Contract.create();
  }

  public static any(data: any): Contract {
    return Contract.create(pond.Contract.fromObject(cloneDeep(data)));
  }

  public key(): string {
    if (this) {
      return this.objKey;
    } else {
      return "";
    }
  }

  public name(): string {
    if (this) {
      return this.title;
    } else {
      return "";
    }
  }

  public objectType(): pond.ObjectType {
    return pond.ObjectType.OBJECT_TYPE_CONTRACT;
  }

  public objectTypeString(): string {
    return "Contract";
  }

  //possibly add another field to the contract for the subtype
  public subtype(): string {
    return this.settings.customCommodity;
  }

  //this is for grain transaction to compare if the source type matches
  public storage(): pond.BinStorage {
    let storage = pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN;
    if (this.settings.commodity === pond.Grain.GRAIN_CUSTOM) {
      storage = pond.BinStorage.BIN_STORAGE_UNSUPPORTED_GRAIN;
    }
    return storage;
  }

  public grain(): pond.Grain {
    return this.settings.commodity;
  }

  public customType(): string {
    return this.settings.customCommodity;
  }

  public isCustom(): boolean {
    let custom = false;
    switch (this.settings.type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        return this.settings.commodity === pond.Grain.GRAIN_CUSTOM;
    }
    return custom;
  }

  public static customValue(type: pond.ContractType): number {
    let v = 0;
    switch (type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        return pond.Grain.GRAIN_CUSTOM;
    }
    return v;
  }

  public static commodityOptionsForType(type: pond.ContractType): Option[] {
    let ops: Option[] = [];
    if (type === pond.ContractType.CONTRACT_TYPE_GRAIN) {
      ops = GrainOptions();
    }
    return ops;
  }

  public static typeOptions(): Option[] {
    return [
      {
        label: "Grain",
        value: pond.ContractType.CONTRACT_TYPE_GRAIN
      }
    ];
  }

  public static toTypeOption(type: pond.ContractType): Option {
    switch (type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        return {
          label: "Grain",
          value: pond.ContractType.CONTRACT_TYPE_GRAIN
        };
      default:
        return {
          label: "Unknown",
          value: pond.ContractType.CONTRACT_TYPE_UNKNOWN
        };
    }
  }

  public static toCommodityOption(type: pond.ContractType, commodity: number): Option {
    switch (type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        return ToGrainOption(commodity);
      default:
        return { label: "None", value: 0 };
    }
  }

  /**
   * returns the conversion value in the settings, if not set will return 1
   * @returns 1 or bushels per tonne
   */
  public conversionValue(): number {
    //trying to avoid a divide by 0 error by only returning a value greater than 0
    //since to get the secondary unit you divide the current unit by the conversion value
    if (this.settings.conversionValue > 0) {
      return this.settings.conversionValue;
    } else {
      return 1;
    }
  }

  //used for grain transactions to compare the source and destinations since different bushels per tonne for custom grain should be treated like seperate grain types
  public bushelsPerTonne(): number {
    return this.conversionValue();
  }

  public sizeInPreferredUnit(): number {
    let size = this.settings.size;
    switch (this.settings.type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && this.conversionValue() > 1) {
          size = size / this.conversionValue();
        }
    }
    return Math.round(size * 100) / 100;
  }

  public deliveredInPreferredUnit(): number {
    let del = this.settings.delivered;
    switch (this.settings.type) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && this.conversionValue() > 1) {
          del = del / this.conversionValue();
        }
    }
    return Math.round(del * 100) / 100;
  }

  public static toStoredUnit(
    secondaryUnitVal: number,
    contractType: pond.ContractType,
    conversionValue: number
  ): number {
    let storedunitVal = secondaryUnitVal;
    switch (contractType) {
      case pond.ContractType.CONTRACT_TYPE_GRAIN:
        if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && conversionValue > 1) {
          storedunitVal = secondaryUnitVal * conversionValue;
        }
    }
    return storedunitVal;
  }

  public grainName(): string {
    if (this.grain() !== pond.Grain.GRAIN_INVALID) {
      if (this.grain() === pond.Grain.GRAIN_CUSTOM) {
        return this.customType();
      } else {
        return GrainDescriber(this.grain()).name;
      }
    } else {
      return "None";
    }
  }
}
