import { useApolloClient, useLazyQuery, useMutation, useQuery } from "@apollo/client/react";
import { gql } from "@apollo/client";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Col, Row } from "antd";
import Axios from "axios";
import _ from "lodash";
import queryString from "query-string";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
  DELETE_AVAILABLE_JOB,
  QUERY_AVAILABLE_JOBS,
  QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
} from "../../graphql/available-jobs.queries";
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import confirmDialog from "../../utils/asyncConfirm";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.container";
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
import HeaderFields from "./jobs-available-supplement.headerfields";
import JobsAvailableTableComponent from "./jobs-available-table.component";

const mapStateToProps = createStructuredSelector({
  bodyshop: selectBodyshop,
  currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
  insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
});

export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail }) {
  const {
    treatments: { CriticalPartsScanning }
  } = useTreatmentsWithConfig({
    attributes: {},
    names: ["CriticalPartsScanning"],
    splitKey: bodyshop.imexshopid
  });
  const notification = useNotification();

  const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });
  const { clm_no, availableJobId } = queryString.parse(useLocation().search);
  const history = useNavigate();
  const { t } = useTranslation();

  const [ownerModalVisible, setOwnerModalVisible] = useState(false);
  const [jobModalVisible, setJobModalVisible] = useState(false);

  const [selectedJob, setSelectedJob] = useState(null);
  const [selectedOwner, setSelectedOwner] = useState(null);
  const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
  const [updateSchComp, setSchComp] = useState({
    actual_in: dayjs(),
    checked: false,
    scheduled_completion: dayjs(),
    automatic: false
  });

  const [insertLoading, setInsertLoading] = useState(false);

  const [insertNote] = useMutation(INSERT_NEW_NOTE);
  const [deleteJob] = useMutation(DELETE_AVAILABLE_JOB);
  const [updateJob] = useMutation(UPDATE_JOB);

  const [insertNewJob] = useMutation(INSERT_NEW_JOB);
  const client = useApolloClient();

  const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK);
  const [loadEstData, estDataRaw] = estDataLazyLoad;

  const importOptionsState = useState({ overrideHeaders: false });
  const importOptions = importOptionsState[0];
  const modalSearchState = useState("");

  //Import Scenario
  const onOwnerFindModalOk = async (lazyData) => {
    logImEXEvent("job_import_new");

    setOwnerModalVisible(false);

    setInsertLoading(true);
    const estData = replaceEmpty(lazyData?.available_jobs_by_pk || estDataRaw?.data?.available_jobs_by_pk);

    if (!(estData && estData.est_data)) {
      //We don't have the right data. Error!
      setInsertLoading(false);
      notification.error({
        title: t("jobs.errors.creating", { error: "No job data present." })
      });
      return;
    }
    // if (process.env.VITE_APP_COUNTRY === "USA") {
    //Massage the CCC file set to remove duplicate UNQ_SEQ.
    InstanceRenderManager({
      executeFunction: true,
      rome: ResolveCCCLineIssues,
      args: [estData.est_data, bodyshop]
    });

    //   } else {
    //IO-539 Check for Parts Rate on PAL for SGI use case.

    await InstanceRenderManager({
      executeFunction: true,
      imex: CheckTaxRates,
      rome: CheckTaxRatesUSA,

      args: [estData.est_data, bodyshop]
    });

    let existingVehicles;
    if (estData.est_data.v_vin) {
      //There's vehicle data, need to double-check the VIN.
      existingVehicles = await client.query({
        query: SEARCH_VEHICLE_BY_VIN,
        variables: {
          vin: estData.est_data.v_vin || estData.est_data.vehicle.data.v_vin
        }
      });
    }

    const newJob = {
      ...estData.est_data,
      date_open: dayjs(),
      status: bodyshop.md_ro_statuses.default_imported,
      notes: {
        data: {
          created_by: currentUser.email,
          audit: true,
          text: t("jobs.labels.importnote")
        }
      },
      queued_for_parts: partsQueueToggle,
      ...(existingVehicles && existingVehicles.data.vehicles.length > 0
        ? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
        : {})
    };

    if (currentUser?.email) {
      newJob.created_user_email = currentUser.email;
    }

    if (selectedOwner) {
      newJob.ownerid = selectedOwner;
      delete newJob.owner;
    }
    if (newJob.vehicleid) {
      delete newJob.vehicle;
    }

    if (typeof newJob.kmin === "string") {
      newJob.kmin = null;
    }

    try {
      const r = await insertNewJob({
        variables: {
          job: newJob
        }
      });
      if (CriticalPartsScanning.treatment === "on") {
        await CriticalPartsScan(r.data.insert_jobs.returning[0].id, notification);
      }
      await Axios.post("/job/totalsssu", {
        id: r.data.insert_jobs.returning[0].id
      });

      notification.success({
        title: t("jobs.successes.created"),
        onClick: () => {
          history(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
        }
      });
      //Job has been inserted. Clean up the available jobs record.

      insertAuditTrail({
        jobid: r.data.insert_jobs.returning[0].id,
        operation: AuditTrailMapping.jobimported(),
        type: "jobimported"
      });

      await deleteJob({
        variables: { id: estData.id }
      }).then(() => {
        refetch();
        setInsertLoading(false);
      });

      setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
    } catch (r) {
      //error while inserting
      notification.error({
        title: t("jobs.errors.creating", { error: r.message })
      });
      refetch().catch((err) => {
        console.error(`Something went wrong in jobs available table container - ${err.message || ""}`);
      });
      setInsertLoading(false);
      setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
    }
  };

  //Supplement scenario
  const onJobFindModalOk = async () => {
    logImEXEvent("job_import_supplement");

    setJobModalVisible(false);
    setInsertLoading(true);

    const estData = estDataRaw?.data?.available_jobs_by_pk;

    if (!(estData && estData.est_data)) {
      //We don't have the right data. Error!
      setInsertLoading(false);
      notification.error({
        title: t("jobs.errors.creating", { error: "No job data present." })
      });
    } else {
      //create upsert job
      let supp = replaceEmpty({ ...estData.est_data });
      //IO-539 Check for Parts Rate on PAL for SGI use case.

      await InstanceRenderManager({
        executeFunction: true,
        rome: ResolveCCCLineIssues,

        args: [supp, bodyshop]
      });

      await InstanceRenderManager({
        executeFunction: true,
        imex: CheckTaxRates,
        rome: CheckTaxRatesUSA,

        args: [supp, bodyshop]
      });

      delete supp.owner;
      delete supp.vehicle;
      delete supp.ins_co_nm;
      if (!importOptions.overrideHeaders) {
        HeaderFields.forEach((item) => delete supp[item]);
      }

      let suppDelta = await GetSupplementDelta(client, selectedJob, supp.joblines.data);

      delete supp.joblines;
      if (suppDelta !== null) {
        await client.mutate({
          mutation: gql`
            ${suppDelta}
          `
        });
      }
      const updateResult = await updateJob({
        variables: {
          jobId: selectedJob,
          job: {
            ...supp,
            queued_for_parts: partsQueueToggle
          }
        }
      });

      setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);

      if (CriticalPartsScanning.treatment === "on") {
        CriticalPartsScan(updateResult.data.update_jobs.returning[0].id, notification);
      }

      if (updateResult.errors) {
        //error while inserting
        notification.error({
          title: t("jobs.errors.creating", {
            error: JSON.stringify(updateResult.errors)
          })
        });
        refetch();
        setInsertLoading(false);
        return;
      }

      const newTotals = await Axios.post("/job/totalsssu", {
        id: selectedJob
      });

      if (newTotals.status !== 200) {
        notification.error({
          title: t("jobs.errors.totalscalc")
        });
        setInsertLoading(false);
        return;
      }
      notification.success({
        title: t("jobs.successes.supplemented"),
        onClick: () => {
          history(`/manage/jobs/${updateResult.data.update_jobs.returning[0].id}`);
        }
      });
      //Job has been inserted. Clean up the available jobs record.

      deleteJob({
        variables: { id: estData.id }
      }).then(() => {
        refetch();
        setInsertLoading(false);
      });

      await insertNote({
        variables: {
          noteInput: [
            {
              jobid: selectedJob,
              created_by: currentUser.email,
              audit: true,
              text: t("jobs.labels.supplementnote")
            }
          ]
        }
      });
      insertAuditTrail({
        jobid: selectedJob,
        operation: AuditTrailMapping.jobsupplement(),
        type: "jobsupplement"
      });
    }
  };

  const owner =
    estDataRaw.data &&
    estDataRaw.data.available_jobs_by_pk &&
    estDataRaw.data.available_jobs_by_pk.est_data &&
    estDataRaw.data.available_jobs_by_pk.est_data.owner &&
    estDataRaw.data.available_jobs_by_pk.est_data.owner.data &&
    !estDataRaw.data.available_jobs_by_pk.issupplement
      ? estDataRaw.data.available_jobs_by_pk.est_data.owner.data
      : null;

  const onOwnerModalCancel = () => {
    setOwnerModalVisible(false);
    setSelectedOwner(null);
    setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
  };

  const onJobModalCancel = () => {
    setJobModalVisible(false);
    modalSearchState[1]("");
    setSelectedJob(null);
    setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
  };

  const addJobAsNew = (record) => {
    loadEstData({ variables: { id: record.id } });
    setOwnerModalVisible(true);
  };

  const addJobAsSupp = useCallback((record) => {
    loadEstData({ variables: { id: record.id } });
    modalSearchState[1](record.clm_no);
    setJobModalVisible(true);
  }, []);

  useEffect(() => {
    if (availableJobId && clm_no) addJobAsSupp({ id: availableJobId, clm_no: clm_no });
  }, [addJobAsSupp, availableJobId, clm_no]);

  if (error) return <AlertComponent type="error" title={error.message} />;

  return (
    <LoadingSpinner loading={insertLoading} message={t("jobs.labels.creating_new_job")}>
      <OwnerFindModalContainer
        loading={estDataRaw.loading}
        error={estDataRaw.error}
        owner={owner}
        partsQueueToggle={partsQueueToggle}
        setPartsQueueToggle={setPartsQueueToggle}
        selectedOwner={selectedOwner}
        setSelectedOwner={setSelectedOwner}
        open={ownerModalVisible}
        onOk={onOwnerFindModalOk}
        onCancel={onOwnerModalCancel}
      />
      <JobsFindModalContainer
        loading={estDataRaw.loading}
        error={estDataRaw.error}
        selectedJob={selectedJob}
        setSelectedJob={setSelectedJob}
        importOptionsState={importOptionsState}
        open={jobModalVisible}
        onOk={onJobFindModalOk}
        onCancel={onJobModalCancel}
        modalSearchState={modalSearchState}
        partsQueueToggle={partsQueueToggle}
        setPartsQueueToggle={setPartsQueueToggle}
        updateSchComp={updateSchComp}
        setSchComp={setSchComp}
      />
      {/* {
        currentUser.email.includes("@rome.") || currentUser.email.includes("@imex.") ? (
        <Button
          onClick={async () => {
            for (const record of data.available_jobs) {
              //Query the data
              console.log("Start Job", record.id);
              const { data } = await loadEstData({
                variables: { id: record.id }
              });
              console.log("Query has been awaited and is complete");
              await onOwnerFindModalOk(data);
            }
          }}
        >
          Add all jobs as new.
        </Button>
      ) : null
      } */}
      <Row gutter={[16, 16]}>
        <Col span={24}>
          <JobsAvailableTableComponent
            loading={loading}
            data={data}
            refetch={refetch}
            addJobAsNew={addJobAsNew}
            addJobAsSupp={addJobAsSupp}
          />
        </Col>
        <Col span={24}>
          <JobsAvailableScan refetch={refetch} />
        </Col>
      </Row>
    </LoadingSpinner>
  );
}

export default connect(mapStateToProps, mapDispatchToProps)(JobsAvailableContainer);

function replaceEmpty(someObj, replaceValue = null) {
  const replacer = (key, value) => (value === "" ? replaceValue || null : value);
  //^ because you seem to want to replace (strings) "null" or "undefined" too
  const temp = JSON.stringify(someObj, replacer);
  return JSON.parse(temp);
}

async function CheckTaxRatesUSA(estData) {
  if (!estData.parts_tax_rates?.PAM) {
    estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
  }
}

async function CheckTaxRates(estData, bodyshop) {
  //LKQ Check
  if (
    !estData.parts_tax_rates?.PAL ||
    estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
    estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
  ) {
    const res = await confirmDialog(
      `ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
    );
    if (res) {
      if (!estData.parts_tax_rates.PAL) {
        estData.parts_tax_rates.PAL = {
          prt_discp: 0,
          prt_mktyp: true,
          prt_mkupp: 0,
          prt_type: "PAL"
        };
      }
      estData.parts_tax_rates.PAL.prt_tax_rt = bodyshop.bill_tax_rates.state_tax_rate / 100;
      estData.parts_tax_rates.PAL.prt_tax_in = true;
    }
  }
  //PAC Check
  if (
    !estData.parts_tax_rates?.PAC ||
    estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
    estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
  ) {
    const res = await confirmDialog(
      `ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
    );
    if (res) {
      if (!estData.parts_tax_rates.PAC) {
        estData.parts_tax_rates.PAC = {
          prt_discp: 0,
          prt_mktyp: true,
          prt_mkupp: 0,
          prt_type: "PAC"
        };
      }
      estData.parts_tax_rates.PAC.prt_tax_rt = bodyshop.bill_tax_rates.state_tax_rate / 100;
      estData.parts_tax_rates.PAC.prt_tax_in = true;
    }
  }
  //PAM Check
  if (
    !estData.parts_tax_rates?.PAM ||
    estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
    estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
  ) {
    const res = await confirmDialog(
      `ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
    );
    if (res) {
      if (!estData.parts_tax_rates.PAM) {
        estData.parts_tax_rates.PAM = {
          prt_discp: 0,
          prt_mktyp: true,
          prt_mkupp: 0,
          prt_type: "PAM"
        };
      }
      estData.parts_tax_rates.PAM.prt_tax_rt = bodyshop.bill_tax_rates.state_tax_rate / 100;
      estData.parts_tax_rates.PAM.prt_tax_in = true;
    }
  }

  if (
    !estData.parts_tax_rates?.PAR ||
    estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
    estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
  ) {
    const res = await confirmDialog(
      `ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
    );
    if (res) {
      if (!estData.parts_tax_rates.PAR) {
        estData.parts_tax_rates.PAR = {
          prt_discp: 0,
          prt_mktyp: true,
          prt_mkupp: 0,
          prt_type: "PAR"
        };
      }
      estData.parts_tax_rates.PAR.prt_tax_rt = bodyshop.bill_tax_rates.state_tax_rate / 100;
      estData.parts_tax_rates.PAR.prt_tax_in = true;
    }
  }

  //IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
  //Currently limited to SK shops only.
  //if (bodyshop.region_config === "CA_SK") {
  estData.joblines.data.forEach((jl, index) => {
    if ((jl.part_type === "PASL" || jl.part_type === "PAS") && jl.lbr_op !== "OP11") {
      estData.joblines.data[index].tax_part = jl.lbr_tax;
    }

    //Set markup lines and tax lines as taxable.
    //900510 is a mark up. 900510 is a discount.
    if (jl.db_ref === "900510") {
      estData.joblines.data[index].tax_part = true;
    }
  });
  //}
}

function ResolveCCCLineIssues(estData) {
  //Find all misc amounts, populate them to the act price.
  //This needs to be done before cleansing unq_seq since some misc prices could move over.
  estData.joblines.data.forEach((line) => {
    if (line.misc_amt && line.misc_amt !== 0) {
      line.act_price = line.act_price + line.misc_amt;
      line.tax_part = !!line.misc_tax;
    }
    //WEB EST SPECIFIC CLEAN UP
    InstanceRenderManager({
      executeFunction: true,
      args: [],
      rome: () => {
        if (line.mod_lbr_ty === "LAET" || line.mod_lbr_ty === "LAUT") {
          //  line.notes += ` | ET/UT Update (prev = ${line.mod_lbr_ty})`;
          line.mod_lbr_ty = "LAR";
        }
        if (line.mod_lbr_ty === "OTSL") {
          line.mod_lbr_ty = line.mod_lbr_hrs === 0 ? null : "LAB";
        }
      }
    });
  });

  InstanceRenderManager({
    executeFunction: true,
    args: [],
    rome: () => {
      //Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
      const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
      const duplicatedUnqSeq = Object.keys(unqSeqHash).filter((key) => unqSeqHash[key].length > 1);

      duplicatedUnqSeq.forEach((unq_seq) => {
        //Keys are strings, convert to int.
        const int_unq_seq = parseInt(unq_seq);

        //When line splitting, the first line is always the non-refinish line. We will keep it as is.
        //We will cleanse the second line, which is always the next line.
        const nonRefLineIndex = estData.joblines.data.findIndex((line) => line.unq_seq === int_unq_seq);
        estData.joblines.data[nonRefLineIndex + 1] = {
          ...estData.joblines.data[nonRefLineIndex + 1],
          part_type: null,
          act_price: 0,
          db_price: 0,
          prt_dsmk_p: 0,
          prt_dsmk_m: 0
        };
      });
    }
  });
}
