import { useApolloClient, useMutation, useQuery } from "@apollo/client/react";
import { Form, Modal } from "antd";
import dayjs from "../../utils/day";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { INSERT_NEW_PARTS_ORDERS, QUERY_PARTS_ORDER_OEC } from "../../graphql/parts-orders.queries";
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext, toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectPartsOrder } from "../../redux/modals/modals.selectors";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import AlertComponent from "../alert/alert.component";
import PartsOrderModalComponent from "./parts-order-modal.component";
import axios from "axios";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import _ from "lodash";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { buildSubmittedPartsOrderLines, getSubmittedPartsOrderJobLineIds } from "./parts-order-modal.utils.js";

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser,
  bodyshop: selectBodyshop,
  partsOrderModal: selectPartsOrder
});

const mapDispatchToProps = (dispatch) => ({
  toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
  setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
  insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
});

export function PartsOrderModalContainer({
  partsOrderModal,
  toggleModalVisible,
  currentUser,
  bodyshop,
  setBillEnterContext,
  insertAuditTrail
}) {
  const { t } = useTranslation();
  const client = useApolloClient();
  const notification = useNotification();

  const {
    treatments: { OEConnection_PriceChange }
  } = useTreatmentsWithConfig({
    attributes: {},
    names: ["OEConnection_PriceChange"],
    splitKey: bodyshop.imexshopid
  });

  const { open, context, actions } = partsOrderModal;
  const { jobId, linesToOrder, isReturn, vendorId, returnFromBill, invoiceNumber, job } = context;

  const { refetch } = actions;
  const [form] = Form.useForm();
  const [saving, setSaving] = useState(false);
  const sendTypeState = useState("e");
  const sendType = sendTypeState[0];

  const { error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
    skip: !open,
    variables: { jobId: jobId },
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });

  const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
  const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
  const [updateJob] = useMutation(UPDATE_JOB);

  const handleFinish = async ({ order_type, removefrompartsqueue, is_quote, ...values }) => {
    logImEXEvent("parts_order_insert");
    setSaving(true);

    // Force job_line_id from context so it never gets dropped by AntD form submission behavior.
    const submittedLines = values?.parts_order_lines?.data ?? [];
    const forcedLines = buildSubmittedPartsOrderLines({
      submittedLines,
      linesToOrder,
      isReturn
    });

    let insertResult;
    try {
      insertResult = await insertPartOrder({
        variables: {
          po: [
            {
              ...values,
              order_date: dayjs().format("YYYY-MM-DD"),
              orderedby: currentUser.email,
              jobid: jobId,
              user_email: currentUser.email,
              return: isReturn,
              status: is_quote
                ? bodyshop.md_order_statuses.default_quote || "Quote"
                : bodyshop.md_order_statuses.default_ordered || "Ordered*",

              // override nested lines to guarantee linkage
              parts_order_lines: { data: forcedLines }
            }
          ]
        },
        refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"]
      });

      if (insertResult.errors) {
        notification.error({
          title: t("parts_orders.errors.creating"),
          description: JSON.stringify(insertResult.errors)
        });
        setSaving(false);
        return;
      }
    } catch (err) {
      notification.error({
        title: t("parts_orders.errors.creating"),
        description: err?.message || String(err)
      });
      console.error("Parts order insert error:", err);
      setSaving(false);
      return;
    }

    notification.success({
      title: isReturn ? t("parts_orders.successes.return_created") : t("parts_orders.successes.created")
    });

    insertAuditTrail({
      jobid: jobId,
      operation: isReturn
        ? AuditTrailMapping.jobspartsreturn(insertResult.data.insert_parts_orders.returning[0].order_number)
        : AuditTrailMapping.jobspartsorder(insertResult.data.insert_parts_orders.returning[0].order_number),
      type: isReturn ? "jobspartsreturn" : "jobspartsorder"
    });

    const jobLineIds = getSubmittedPartsOrderJobLineIds(forcedLines);

    try {
      const jobLinesResult = await updateJobLines({
        variables: {
          ids: jobLineIds,
          status: isReturn
            ? bodyshop.md_order_statuses.default_returned || "Returned*"
            : is_quote
              ? bodyshop.md_order_statuses.default_quote || "Quote"
              : bodyshop.md_order_statuses.default_ordered || "Ordered*"
        }
      });

      if (jobLinesResult.errors) {
        notification.error({
          title: t("parts_orders.errors.updating_status"),
          description: JSON.stringify(jobLinesResult.errors)
        });
      }
    } catch (err) {
      notification.error({
        title: t("parts_orders.errors.updating_status"),
        description: err?.message || String(err)
      });
      console.error("Job lines update error:", err);
    }

    if (!isReturn && removefrompartsqueue) {
      try {
        await updateJob({
          variables: {
            jobId: jobId,
            job: {
              queued_for_parts: false
            }
          }
        });
      } catch (err) {
        console.error("Update job queue error:", err);
      }
    }

    if (values.vendorid === bodyshop.inhousevendorid) {
      // Use linesToOrder for joblineid, and use forcedLines for the user-entered fields
      setBillEnterContext({
        actions: { refetch: refetch },
        context: {
          disableInvNumber: true,
          job: { id: jobId },
          bill: {
            vendorid: bodyshop.inhousevendorid,
            invoice_number: "ih",
            isinhouse: true,
            date: dayjs(),
            total: 0,
            billlines: forcedLines.map((p) => ({
              joblineid: p.job_line_id,
              actual_price: p.act_price,
              actual_cost: 0, // p.act_price,
              line_desc: p.line_desc,
              line_remarks: p.line_remarks,
              part_type: p.part_type,
              quantity: p.quantity || 1,
              applicable_taxes: {
                local: false,
                state: false,
                federal: false
              }
            }))
          }
        }
      });

      setSaving(false);
      toggleModalVisible();
      return;
    }

    if (refetch) refetch();

    const Templates = TemplateList("partsorder", context);

    if (sendType === "e") {
      const matchingVendor = data?.vendors?.find((item) => item.id === values.vendorid);
      const vendorEmails = matchingVendor?.email && matchingVendor.email.split(RegExp("[;,]"));

      GenerateDocument(
        {
          name: isReturn
            ? Templates.parts_return_slip.key
            : order_type === "parts_order"
              ? Templates.parts_order.key
              : Templates.sublet_order.key,
          variables: {
            id: insertResult.data.insert_parts_orders.returning[0].id
          }
        },
        {
          to: matchingVendor ? vendorEmails : null,
          replyTo: bodyshop.email,
          subject: isReturn
            ? Templates.parts_return_slip.subject
            : order_type === "parts_order"
              ? Templates.parts_order.subject
              : Templates.sublet_order.subject
        },
        "e",
        jobId,
        notification
      );
    } else if (sendType === "p") {
      GenerateDocument(
        {
          name: isReturn
            ? Templates.parts_return_slip.key
            : order_type === "parts_order"
              ? Templates.parts_order.key
              : Templates.sublet_order.key,
          variables: {
            id: insertResult.data.insert_parts_orders.returning[0].id
          }
        },
        {},
        "p",
        null,
        notification
      );
    } else if (sendType === "oec") {
      // Send to Partner OEC.
      try {
        const partsOrder = await client.query({
          query: QUERY_PARTS_ORDER_OEC,
          variables: {
            id: insertResult.data.insert_parts_orders.returning[0].id
          }
        });

        let po;
        // Massage the data based on the split. Should they be able to overwrite OEC pricing?
        if (OEConnection_PriceChange.treatment === "on") {
          po = _.cloneDeep(partsOrder.data.parts_orders_by_pk);
          po.parts_order_lines.forEach((pol) => {
            pol.priceChange = true;
          });
        }

        const oecResponse = await axios.post("http://localhost:1337/oec/", po || partsOrder.data.parts_orders_by_pk, {
          headers: {
            Authorization: `Bearer ${await auth.currentUser.getIdToken()}`
          }
        });

        if (oecResponse.data && oecResponse.data.success === false) {
          notification.error({
            title: t("parts_orders.errors.oec", {
              error: oecResponse.data.error
            })
          });
        }
      } catch (err) {
        console.log("Error OEC.", err);
        notification.error({
          title: t("parts_orders.errors.oec", {
            error: JSON.stringify(err?.message || String(err))
          })
        });
        setSaving(false);
        return;
      }
    }

    setSaving(false);
    toggleModalVisible();
  };

  const initialValues = {
    jobid: jobId,
    return: isReturn,
    deliver_by: isReturn ? dayjs(new Date()) : null,
    vendorid: vendorId,
    returnfrombill: returnFromBill,
    parts_order_lines: {
      data: linesToOrder
        ? linesToOrder.reduce((acc, value) => {
            acc.push({
              line_desc: value.line_desc,
              oem_partno: value.oem_partno,
              db_price: value.db_price,
              act_price: value.act_price,
              cost: value.cost,
              quantity: value.part_qty,
              job_line_id: isReturn ? value.joblineid : value.id,
              part_type: value.part_type,
              ...(isReturn && { cm_received: false })
            });
            return acc;
          }, [])
        : []
    }
  };

  useEffect(() => {
    if (open && !!linesToOrder) {
      form.resetFields();
    }
  }, [open, linesToOrder, form]);

  //This used to have a loading component spinner for the vendor data. With Apollo 4, the NetworkState isn't emitting correctly, so loading just gets set to true the second time, and no longer works as expected.
  return (
    <Modal
      open={open}
      title={
        isReturn
          ? `${t("parts_orders.labels.returnpartsorder")} ${invoiceNumber}`
          : t("parts_orders.labels.newpartsorder")
      }
      onCancel={() => toggleModalVisible()}
      onOk={() => form.submit()}
      okButtonProps={{ loading: saving }}
      cancelButtonProps={{ loading: saving }}
      destroyOnHidden
      width="75%"
      forceRender
    >
      {error ? <AlertComponent title={error.message} type="error" /> : null}
      <Form form={form} layout="vertical" autoComplete="no" onFinish={handleFinish} initialValues={initialValues}>
        <PartsOrderModalComponent
          form={form}
          vendorList={data?.vendors || []}
          sendTypeState={sendTypeState}
          isReturn={isReturn}
          preferredMake={data && data.jobs[0] && data.jobs[0].v_make_desc}
          job={job}
        />
      </Form>
    </Modal>
  );
}

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