import { DBFFile } from "dbffile";
import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import YNBoolConverter from "../../util/ynBoolConverter";
import {
  DecodedPfm,
  DecodedPfmLine,
  JobMaterialRateFields,
} from "./decode-pfm.interface";
import { platform } from "@electron-toolkit/utils";
import { findFileCaseInsensitive } from "./decoder-utils";

const DecodePfm = async (
  extensionlessFilePath: string,
): Promise<DecodedPfm> => {
  let dbf: DBFFile | null = null;
  if (platform.isWindows) {
    try {
      dbf = await DBFFile.open(`${extensionlessFilePath}.PFM`);
    } catch (error) {
      //PFM File only has 1 location.
      log.error("Error opening PFM File.", errorTypeCheck(error));
    }

    if (!dbf) {
      log.error(`Could not find any PFM files at ${extensionlessFilePath}`);
      throw new Error(
        `Could not find any PFM files at ${extensionlessFilePath}`,
      );
    }
  } else {
    const possibleExtensions: string[] = [".pfm"];
    const filePath = await findFileCaseInsensitive(
      extensionlessFilePath,
      possibleExtensions,
    );
    try {
      if (!filePath) {
        log.error(`Could not find any PFM files at ${extensionlessFilePath}`);
        throw new Error(
          `Could not find any PFM files at ${extensionlessFilePath}`,
        );
      }
      dbf = await DBFFile.open(filePath);
    } catch (error) {
      log.error("Error opening PFM File.", errorTypeCheck(error));
      throw error;
    }
  }
  const rawDBFRecord = await dbf.readRecords();

  //AD2 will always have only 1 row.
  //Commented lines have been cross referenced with existing partner fields.

  const jobMaterialRates: JobMaterialRateFields = {
    rate_mapa: 0,
    tax_paint_mat_rt: 0,
    rate_mash: 0,
    tax_shop_mat_rt: 0,
    rate_mahw: 0,
    tax_levies_rt: 0,
    rate_ma2s: 0,
    rate_ma2t: 0,
    rate_ma3s: 0,
    rate_macs: 0,
    rate_mabl: 0,
  };

  const rawPfmData: DecodedPfmLine[] = rawDBFRecord.map((record) => {
    const singleLineData: DecodedPfmLine = YNBoolConverter(
      deepLowerCaseKeys(
        _.pick(record, [
          //TODO: Add typings for EMS File Formats.
          "MATL_TYPE",
          "CAL_CODE",
          "CAL_DESC",
          "CAL_MAXDLR",
          "CAL_PRIP",

          "CAL_SECP",
          "MAT_CALP",
          "CAL_PRETHR", //Mitchell here
          "CAL_PSTTHR",
          "CAL_THRAMT",

          "CAL_LBRMIN",

          "CAL_LBRRTE", //Audatex puts it here
          "CAL_OPCODE",

          "TAX_IND",
          "MAT_TAXP",
          "MAT_ADJP",
          "MAT_TX_TY1",
          "MAT_TX_IN1",
          "MAT_TX_TY2",
          "MAT_TX_IN2",
          "MAT_TX_TY3",
          "MAT_TX_IN3",
          "MAT_TX_TY4",
          "MAT_TX_IN4",
          "MAT_TX_TY5",
          "MAT_TX_IN5",
        ]),
      ),
    );

    //Also capture the whole object.
    //This is segmented because the whole object was not previously captured for ImEX as it wasn't needed.
    //Rome needs the whole object to accurately calculate the tax rates.

    return singleLineData;
  });

  //Apply line by line adjustments.
  const mapaLine: DecodedPfmLine | undefined = rawPfmData.find(
    (line) => line.matl_type === "MAPA",
  );
  if (mapaLine) {
    jobMaterialRates.rate_mapa =
      mapaLine.cal_lbrrte || mapaLine.cal_prethr || 0;
    jobMaterialRates.tax_paint_mat_rt = (mapaLine.mat_taxp ?? 0) / 100;
  }

  const mashLine: DecodedPfmLine | undefined = rawPfmData.find(
    (line) => line.matl_type === "MASH",
  );
  if (mashLine) {
    jobMaterialRates.rate_mash =
      mashLine.cal_lbrrte || mashLine.cal_prethr || 0;
    jobMaterialRates.tax_shop_mat_rt = (mashLine.mat_taxp ?? 0) / 100;
  }

  const mahwLine: DecodedPfmLine | undefined = rawPfmData.find(
    (line) => line.matl_type === "MAHW",
  );
  if (mahwLine) {
    jobMaterialRates.rate_mahw =
      mahwLine.cal_lbrrte || mahwLine.cal_prethr || 0;
    jobMaterialRates.tax_levies_rt = (mahwLine.mat_taxp ?? 0) / 100;
  }

  const additionalMaterials = ["MA2S", "MA2T", "MA3S", "MACS", "MABL"];
  additionalMaterials.forEach((type) => {
    const line: DecodedPfmLine | undefined = rawPfmData.find(
      (line) => line.matl_type === type,
    );
    if (line) {
      jobMaterialRates[`rate_${type.toLowerCase()}`] =
        line.cal_lbrrte || line.cal_prethr || 0;
    }
  });

  //Apply business logic transfomrations.
  //We don't have an inspection date, we instead have `date_estimated`

  return {
    ...jobMaterialRates,
    materials: {
      MASH: mashLine,
      MAPA: mapaLine, //TODO: Need to verify if more fields are to come in here.
    },
    //cieca_pfm: rawPfmData, //TODO: Not currently captured. This may have valu in the future.
  };
};

export default DecodePfm;
