import {
  gql,
  useApolloClient,
  useLazyQuery,
  useMutation,
  useQuery,
} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd";
import Axios from "axios";
import Dinero from "dinero.js";
import moment from "moment";
import queryString from "query-string";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
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 confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CriticalPartsScan from "../../utils/criticalPartsScan";
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 }) =>
    dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsAvailableContainer({
  bodyshop,
  currentUser,
  insertAuditTrail,
}) {
  const { CriticalPartsScanning } = useTreatments(
    ["CriticalPartsScanning"],
    {},
    bodyshop.imexshopid
  );
  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 = useHistory();
  const { t } = useTranslation();

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

  const [selectedJob, setSelectedJob] = useState(null);
  const [selectedOwner, setSelectedOwner] = useState(null);
  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 () => {
    logImEXEvent("job_import_new");

    setOwnerModalVisible(false);
    setInsertLoading(true);

    const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);

    if (!(estData && estData.est_data)) {
      //We don't have the right data. Error!
      setInsertLoading(false);
      notification["error"]({
        message: t("jobs.errors.creating", { error: "No job data present." }),
      });
      return;
    }
    //IO-539 Check for Parts Rate on PAL for SGI use case.
    await CheckTaxRates(estData.est_data, bodyshop);

    const newTotals = (
      await Axios.post("/job/totals", {
        job: {
          ...estData.est_data,
          joblines: estData.est_data.joblines.data,
        },
      })
    ).data;

    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,
      clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
      owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
      job_totals: newTotals,
      date_open: moment(),
      status: bodyshop.md_ro_statuses.default_imported,
      notes: {
        data: {
          created_by: currentUser.email,
          audit: true,
          text: t("jobs.labels.importnote"),
        },
      },
      queued_for_parts: true,
      ...(existingVehicles && existingVehicles.data.vehicles.length > 0
        ? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
        : {}),
    };

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

    insertNewJob({
      variables: {
        job: newJob,
      },
    })
      .then((r) => {
        if (CriticalPartsScanning.treatment === "on") {
          CriticalPartsScan(r.data.insert_jobs.returning[0].id);
        }
        notification["success"]({
          message: t("jobs.successes.created"),
          onClick: () => {
            history.push(`/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(),
        });

        deleteJob({
          variables: { id: estData.id },
        }).then((r) => {
          refetch();
          setInsertLoading(false);
        });
      })
      .catch((r) => {
        //error while inserting
        notification["error"]({
          message: t("jobs.errors.creating", { error: r.message }),
        });
        refetch();
        setInsertLoading(false);
      });
  };

  //Suplement 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"]({
        message: 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 CheckTaxRates(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,
            // clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
            // owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
            //   "0.00"
            // ),
            // job_totals: newTotals,
            // queued_for_parts: true,
          },
        },
      });
      if (CriticalPartsScanning.treatment === "on") {
        CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
      }
      if (updateResult.errors) {
        //error while inserting
        notification["error"]({
          message: 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"]({
          message: t("jobs.errors.totalscalc"),
        });
        setInsertLoading(false);
        return;
      }
      notification["success"]({
        message: t("jobs.successes.supplemented"),
        onClick: () => {
          history.push(
            `/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((r) => {
        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(),
      });
    }
  };

  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);
  };

  const onJobModalCancel = () => {
    setJobModalVisible(false);
    modalSearchState[1]("");
    setSelectedJob(null);
  };

  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);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

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

  if (error) return <AlertComponent type="error" message={error.message} />;
  return (
    <LoadingSpinner
      loading={insertLoading}
      message={t("jobs.labels.creating_new_job")}
    >
      <OwnerFindModalContainer
        loading={estDataRaw.loading}
        error={estDataRaw.error}
        owner={owner}
        selectedOwner={selectedOwner}
        setSelectedOwner={setSelectedOwner}
        visible={ownerModalVisible}
        onOk={onOwnerFindModalOk}
        onCancel={onOwnerModalCancel}
      />
      <JobsFindModalContainer
        loading={estDataRaw.loading}
        error={estDataRaw.error}
        selectedJob={selectedJob}
        setSelectedJob={setSelectedJob}
        importOptionsState={importOptionsState}
        visible={jobModalVisible}
        onOk={onJobFindModalOk}
        onCancel={onJobModalCancel}
        modalSearchState={modalSearchState}
      />
      <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 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;
    }
  });
  //}
}
