import { CopyFilled, DeleteFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { Button, Card, Col, Form, Input, message, Row, Space, Spin, Statistic } from "antd";
import axios from "axios";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectCardPayment } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
import { getCurrentUser } from "../../firebase/firebase.utils";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";

const mapStateToProps = createStructuredSelector({
  cardPaymentModal: selectCardPayment,
  bodyshop: selectBodyshop,
  currentUser: getCurrentUser
});

const mapDispatchToProps = (dispatch) => ({
  insertAuditTrail: ({ jobid, operation, type }) =>
    dispatch(
      insertAuditTrail({
        jobid,
        operation,
        type
      })
    ),
  toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
});

const CardPaymentModalComponent = ({
  bodyshop,
  currentUser,
  cardPaymentModal,
  toggleModalVisible,
  insertAuditTrail
}) => {
  const { context, actions } = cardPaymentModal;

  const [form] = Form.useForm();
  const [paymentLink, setPaymentLink] = useState();
  const [loading, setLoading] = useState(false);
  const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
  const { t } = useTranslation();
  const notification = useNotification();

  const [, { data, refetch, queryLoading }] = useLazyQuery(QUERY_RO_AND_OWNER_BY_JOB_PKS, {
    variables: { jobids: [context.jobid] },
    skip: !context?.jobid
  });

  const collectIPayFields = () => {
    const iPayFields = document.querySelectorAll(".ipayfield");
    const iPayData = {};
    iPayFields.forEach((field) => {
      iPayData[field.dataset.ipayname] = field.value;
    });
    return iPayData;
  };

  const SetIntellipayCallbackFunctions = () => {
    console.log("*** Set IntelliPay callback functions.");

    window.intellipay.runOnClose(() => {
      //window.intellipay.initialize();
    });

    window.intellipay.runOnApproval(() => {
      //2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
      //Add a slight delay to allow the refetch to properly get the data.
      setTimeout(() => {
        if (actions?.refetch) actions.refetch();
        setLoading(false);
        toggleModalVisible();
      }, 750);
    });

    window.intellipay.runOnNonApproval(async (response) => {
      // Mutate unsuccessful payment

      const { payments } = form.getFieldsValue();
      await insertPaymentResponse({
        variables: {
          paymentResponse: payments.map((payment) => ({
            amount: payment.amount,
            bodyshopid: bodyshop.id,
            jobid: payment.jobid,
            declinereason: response.declinereason,
            ext_paymentid: response.paymentid.toString(),
            successful: false,
            response
          }))
        }
      });

      payments.forEach((payment) =>
        insertAuditTrail({
          jobid: payment.jobid,
          operation: AuditTrailMapping.failedpayment(),
          type: "failedpayment"
        })
      );
    });
  };

  const handleIntelliPayCharge = async () => {
    setLoading(true);
    //Validate
    try {
      await form.validateFields();
    } catch {
      setLoading(false);
      return;
    }

    const iPayData = collectIPayFields();
    const { payments } = form.getFieldsValue();

    try {
      const response = await axios.post("/intellipay/lightbox_credentials", {
        bodyshop,
        refresh: !!window.intellipay,
        paymentSplitMeta: form.getFieldsValue(),
        iPayData: iPayData,
        comment: btoa(JSON.stringify({ payments, userEmail: currentUser.email }))
      });

      if (window.intellipay) {
        eval(response.data);
        pollForIntelliPay(() => {
          SetIntellipayCallbackFunctions();
          window.intellipay.autoOpen();
        });
      } else {
        const rg = document.createRange();
        const node = rg.createContextualFragment(response.data);
        document.documentElement.appendChild(node);
        pollForIntelliPay(() => {
          SetIntellipayCallbackFunctions();
          window.intellipay.isAutoOpen = true;
          window.intellipay.initialize();
        });
      }
    } catch {
      notification.open({
        type: "error",
        message: t("job_payments.notifications.error.openingip")
      });
      setLoading(false);
    }
  };

  const handleIntelliPayChargeShortLink = async () => {
    setLoading(true);
    //Validate
    try {
      await form.validateFields();
    } catch {
      setLoading(false);
      return;
    }

    const iPayData = collectIPayFields();

    try {
      const { payments } = form.getFieldsValue();
      const response = await axios.post("/intellipay/generate_payment_url", {
        bodyshop,
        amount: payments.reduce((acc, val) => acc + (val?.amount || 0), 0),
        account: payments && data?.jobs?.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null,
        comment: btoa(JSON.stringify({ payments, userEmail: currentUser.email })),
        paymentSplitMeta: form.getFieldsValue(),
        iPayData: iPayData
      });

      if (response?.data?.shorUrl) {
        setPaymentLink(response.data.shorUrl);
        await navigator.clipboard.writeText(response.data.shorUrl);
        message.success(t("general.actions.copied"));
      }
      setLoading(false);
    } catch {
      notification.open({
        type: "error",
        message: t("job_payments.notifications.error.openingip")
      });
      setLoading(false);
    }
  };

  return (
    <Card title="Card Payment">
      <Spin spinning={loading}>
        <Form
          form={form}
          layout="vertical"
          initialValues={{
            payments: context.jobid ? [{ jobid: context.jobid }] : []
          }}
        >
          <Form.List name={["payments"]}>
            {(fields, { add, remove }) => (
              <div>
                {fields.map((field, index) => (
                  <Form.Item key={field.key}>
                    <Row gutter={[16, 16]}>
                      <Col span={16}>
                        <Form.Item
                          key={`${index}jobid`}
                          label={t("jobs.fields.ro_number")}
                          name={[field.name, "jobid"]}
                          rules={[{ required: true }]}
                        >
                          <JobSearchSelectComponent notExported={false} clm_no />
                        </Form.Item>
                      </Col>
                      <Col span={6}>
                        <Form.Item
                          key={`${index}amount`}
                          label={t("payments.fields.amount")}
                          name={[field.name, "amount"]}
                          rules={[{ required: true }]}
                        >
                          <CurrencyFormItemComponent />
                        </Form.Item>
                      </Col>
                      <Col span={2}>
                        <DeleteFilled style={{ margin: "1rem" }} onClick={() => remove(field.name)} />
                      </Col>
                    </Row>
                  </Form.Item>
                ))}
                <Form.Item>
                  <Button type="dashed" onClick={() => add()} style={{ width: "100%" }}>
                    {t("general.actions.add")}
                  </Button>
                </Form.Item>
              </div>
            )}
          </Form.List>

          <Form.Item
            shouldUpdate={(prevValues, curValues) =>
              prevValues.payments?.map((p) => p?.jobid + p?.amount).join() !==
              curValues.payments?.map((p) => p?.jobid + p?.amount).join()
            }
          >
            {() => {
              //If all of the job ids have been fileld in, then query and update the IP field.
              const { payments } = form.getFieldsValue();
              if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
                refetch({ jobids: payments.map((p) => p.jobid) });
              }
              return (
                <>
                  <Input
                    className="ipayfield"
                    data-ipayname="account"
                    type="hidden"
                    value={
                      payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null
                    }
                  />
                  <Input
                    className="ipayfield"
                    data-ipayname="email"
                    type="hidden"
                    value={
                      payments && data && data.jobs.length > 0 ? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea : null
                    }
                  />
                </>
              );
            }}
          </Form.Item>
          <Form.Item
            shouldUpdate={(prevValues, curValues) =>
              prevValues.payments?.map((p) => p?.amount).join() !== curValues.payments?.map((p) => p?.amount).join()
            }
          >
            {() => {
              const { payments } = form.getFieldsValue();
              const totalAmountToCharge = payments?.reduce((acc, val) => acc + (val?.amount || 0), 0);
              return (
                <Space style={{ float: "right" }}>
                  <Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
                  <Input
                    className="ipayfield"
                    data-ipayname="amount"
                    type="hidden"
                    value={totalAmountToCharge?.toFixed(2)}
                  />
                  <Input
                    className="ipayfield"
                    data-ipayname="comment"
                    type="hidden"
                    value={btoa(JSON.stringify(payments))}
                    hidden
                  />
                  <Button
                    type="primary"
                    //  data-ipayname="submit"
                    className="ipayfield"
                    loading={queryLoading || loading}
                    disabled={!(totalAmountToCharge > 0)}
                    onClick={handleIntelliPayCharge}
                  >
                    {t("job_payments.buttons.proceedtopayment")}
                  </Button>
                  <Space direction="vertical" align="center">
                    <Button
                      type="primary"
                      //  data-ipayname="submit"
                      className="ipayfield"
                      loading={queryLoading || loading}
                      disabled={!(totalAmountToCharge > 0)}
                      onClick={handleIntelliPayChargeShortLink}
                    >
                      {t("job_payments.buttons.create_short_link")}
                    </Button>
                  </Space>
                </Space>
              );
            }}
          </Form.Item>
        </Form>
        {paymentLink && (
          <Space
            style={{ cursor: "pointer", float: "right" }}
            align="end"
            onClick={() => {
              navigator.clipboard.writeText(paymentLink);
              message.success(t("general.actions.copied"));
            }}
          >
            <div>{paymentLink}</div>
            <CopyFilled />
          </Space>
        )}
      </Spin>
    </Card>
  );
};

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

//Poll for window.IntelliPay.fixAmount for 5 seconds. If it doesn't come up, just try anyways to force the possible error.
function pollForIntelliPay(callbackFunction) {
  const timeout = 5000;
  const interval = 150; // Poll every 100 milliseconds
  const startTime = Date.now();

  function checkFixAmount() {
    if (window.intellipay?.fixAmount) {
      callbackFunction();
      return;
    }

    if (Date.now() - startTime >= timeout) {
      console.log("Stopped polling IntelliPay after 10 seconds. Attemping to set functions anyways.");
      callbackFunction();
      return;
    }

    setTimeout(checkFixAmount, interval);
  }

  checkFixAmount();
}
