import { DeleteFilled } from "@ant-design/icons";
import { useMutation, useQuery } from "@apollo/client/react";
import { Button, Card, Col, Form, Input, InputNumber, Row, Select, Skeleton, Space, Switch, Typography } from "antd";

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 { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import InlineValidatedFormRow from "../layout-form-row/inline-validated-form-row.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import {
  INLINE_TITLE_GROUP_STYLE,
  INLINE_TITLE_HANDLE_STYLE,
  INLINE_TITLE_INPUT_STYLE,
  INLINE_TITLE_LABEL_STYLE,
  INLINE_TITLE_ROW_STYLE,
  INLINE_TITLE_SEPARATOR_STYLE,
  INLINE_TITLE_SWITCH_GROUP_STYLE,
  InlineTitleListIcon
} from "../layout-form-row/inline-form-row-title.utils.js";

import {
  INSERT_EMPLOYEE_TEAM,
  QUERY_EMPLOYEE_TEAM_BY_ID,
  UPDATE_EMPLOYEE_TEAM
} from "../../graphql/employee_teams.queries";
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import {
  getSplitTotal,
  hasExactSplitTotal,
  LABOR_TYPES,
  normalizeEmployeeTeam,
  validateEmployeeTeamMembers
} from "./shop-employee-teams.form.utils.js";

const mapStateToProps = createStructuredSelector({
  bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});

const PAYOUT_METHOD_OPTIONS = [
  { labelKey: "employee_teams.options.hourly", value: "hourly" },
  { labelKey: "employee_teams.options.commission_percentage", value: "commission" }
];

const TEAM_MEMBER_RATE_FIELD_COLS = { xs: 24, sm: 12, md: 8, lg: 6, xxl: 4 };

const formatAllocationPercentage = (percentage) => {
  if (percentage === null || percentage === undefined || percentage === "") return null;

  const numericValue = Number(percentage);
  if (!Number.isFinite(numericValue)) return null;

  return `${numericValue.toFixed(2).replace(/\.?0+$/, "")}%`;
};

export function ShopEmployeeTeamsFormComponent({ bodyshop, form, onDirtyChange, isDirty }) {
  const { t } = useTranslation();
  const [internalForm] = Form.useForm();
  const [internalIsDirty, setInternalIsDirty] = useState(false);
  const teamForm = form ?? internalForm;
  const resolvedIsDirty = typeof isDirty === "boolean" ? isDirty : internalIsDirty;
  const history = useNavigate();
  const search = querystring.parse(useLocation().search);
  const notification = useNotification();
  const [hydratedTeamId, setHydratedTeamId] = useState(search.employeeTeamId === "new" ? "new" : null);
  const isNewTeam = search.employeeTeamId === "new";

  const { error, data, loading, refetch } = useQuery(QUERY_EMPLOYEE_TEAM_BY_ID, {
    variables: { id: search.employeeTeamId },
    skip: !search.employeeTeamId || isNewTeam,
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only",
    notifyOnNetworkStatusChange: true
  });

  const currentTeamData = data?.employee_teams_by_pk?.id === search.employeeTeamId ? data.employee_teams_by_pk : null;

  const updateDirtyState = useCallback(
    (nextDirtyState) => {
      setInternalIsDirty(nextDirtyState);
      onDirtyChange?.(nextDirtyState);
    },
    [onDirtyChange]
  );

  const clearTeamFormMeta = useCallback(() => {
    const fieldMeta = teamForm.getFieldsError().map(({ name }) => ({
      name,
      touched: false,
      validating: false,
      errors: [],
      warnings: []
    }));

    if (fieldMeta.length > 0) {
      teamForm.setFields(fieldMeta);
    }

    updateDirtyState(false);
  }, [teamForm, updateDirtyState]);

  const resetTeamFormToCurrentData = useCallback(() => {
    let hydrationFrameId;

    teamForm.resetFields();

    if (isNewTeam) {
      setHydratedTeamId("new");
      hydrationFrameId = window.requestAnimationFrame(() => {
        clearTeamFormMeta();
      });
      return () => {
        if (hydrationFrameId) window.cancelAnimationFrame(hydrationFrameId);
      };
    }

    setHydratedTeamId(null);

    if (loading) {
      return undefined;
    }

    if (currentTeamData) {
      teamForm.setFieldsValue(normalizeEmployeeTeam(currentTeamData));
    }

    hydrationFrameId = window.requestAnimationFrame(() => {
      setHydratedTeamId(search.employeeTeamId);
      clearTeamFormMeta();
    });

    return () => {
      if (hydrationFrameId) window.cancelAnimationFrame(hydrationFrameId);
    };
  }, [clearTeamFormMeta, currentTeamData, isNewTeam, loading, search.employeeTeamId, teamForm]);

  useEffect(() => resetTeamFormToCurrentData(), [resetTeamFormToCurrentData]);

  const [updateEmployeeTeam] = useMutation(UPDATE_EMPLOYEE_TEAM);
  const [insertEmployeeTeam] = useMutation(INSERT_EMPLOYEE_TEAM);
  const payoutMethodOptions = PAYOUT_METHOD_OPTIONS.map(({ labelKey, value }) => ({
    label: t(labelKey),
    value
  }));
  const teamName = Form.useWatch("name", teamForm);
  const teamMembers = Form.useWatch(["employee_team_members"], teamForm) || [];
  const isTeamHydrating = !isNewTeam && Boolean(search.employeeTeamId) && hydratedTeamId !== search.employeeTeamId;
  const isAllocationTotalExact = hasExactSplitTotal(teamMembers);
  const allocationTotalValue = formatAllocationPercentage(getSplitTotal(teamMembers))?.replace("%", "") || "0";
  const teamNameDisplay = teamName?.trim() || t("employee_teams.fields.name");
  const teamCardTitle = isTeamHydrating ? (
    t("employee_teams.fields.name")
  ) : (
    <span>
      <span>{teamNameDisplay}</span>
      <span> - </span>
      <Typography.Text type={isAllocationTotalExact ? undefined : "danger"}>
        {t("employee_teams.labels.allocation_total", {
          total: allocationTotalValue
        })}
      </Typography.Text>
    </span>
  );

  const handleFinish = async ({ employee_team_members = [], ...values }) => {
    const { normalizedTeamMembers, errorKey } = validateEmployeeTeamMembers(employee_team_members);

    if (errorKey) {
      notification.error({
        title: t(errorKey)
      });
      return;
    }

    if (search.employeeTeamId && search.employeeTeamId !== "new") {
      logImEXEvent("shop_employee_update");

      const result = await updateEmployeeTeam({
        variables: {
          employeeTeamId: search.employeeTeamId,
          employeeTeam: values,
          teamMemberUpdates: normalizedTeamMembers
            .filter((teamMember) => teamMember.id)
            .map((teamMember) => ({
              where: { id: { _eq: teamMember.id } },
              _set: teamMember
            })),
          teamMemberInserts: normalizedTeamMembers
            .filter((teamMember) => teamMember.id === null || teamMember.id === undefined)
            .map((teamMember) => ({ ...teamMember, teamid: search.employeeTeamId })),
          teamMemberDeletes: data.employee_teams_by_pk.employee_team_members
            .filter(
              (teamMember) => !normalizedTeamMembers.find((currentTeamMember) => currentTeamMember.id === teamMember.id)
            )
            .map((teamMember) => teamMember.id)
        }
      });

      if (!result.errors) {
        updateDirtyState(false);
        void refetch();
        notification.success({
          title: t("employees.successes.save")
        });
      } else {
        notification.error({
          title: t("employees.errors.save", {
            message: JSON.stringify(error)
          })
        });
      }
    } else {
      logImEXEvent("shop_employee_insert");

      insertEmployeeTeam({
        variables: {
          employeeTeam: {
            ...values,
            employee_team_members: { data: normalizedTeamMembers },
            bodyshopid: bodyshop.id
          }
        },
        refetchQueries: ["QUERY_TEAMS"]
      }).then((response) => {
        updateDirtyState(false);
        search.employeeTeamId = response.data.insert_employee_teams_one.id;
        history({ search: querystring.stringify(search) });
        notification.success({
          title: t("employees.successes.save")
        });
      });
    }
  };

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

  return (
    <Card
      title={isTeamHydrating ? undefined : teamCardTitle}
      extra={
        <Button
          type="primary"
          onClick={() => teamForm.submit()}
          disabled={isTeamHydrating || !resolvedIsDirty}
          style={{ minWidth: 190 }}
        >
          {t("employee_teams.actions.save_team")}
        </Button>
      }
    >
      {isTeamHydrating ? (
        <Skeleton active title={false} paragraph={{ rows: 12 }} />
      ) : (
        <Form
          onFinish={handleFinish}
          autoComplete={"off"}
          layout="vertical"
          form={teamForm}
          onValuesChange={() => {
            updateDirtyState(teamForm.isFieldsTouched());
          }}
        >
          <FormsFieldChanged form={teamForm} onReset={resetTeamFormToCurrentData} onDirtyChange={updateDirtyState} />
          <LayoutFormRow
            title={
              <div
                style={{
                  ...INLINE_TITLE_ROW_STYLE,
                  justifyContent: "space-between"
                }}
              >
                <div
                  style={{
                    whiteSpace: "nowrap",
                    fontWeight: 500,
                    fontSize: "var(--ant-font-size-lg)",
                    lineHeight: 1.2,
                    marginRight: "auto"
                  }}
                >
                  {t("employee_teams.labels.team_options")}
                </div>
                <div
                  style={{
                    ...INLINE_TITLE_SWITCH_GROUP_STYLE,
                    marginLeft: "auto"
                  }}
                >
                  <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
                  <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.active")}</div>
                  <Form.Item noStyle name="active" valuePropName="checked">
                    <Switch />
                  </Form.Item>
                </div>
              </div>
            }
            wrapTitle
          >
            <Form.Item
              name="name"
              label={t("employee_teams.fields.name")}
              rules={[
                {
                  required: true
                }
              ]}
            >
              <Input />
            </Form.Item>
            <Form.Item
              label={t("employee_teams.fields.max_load")}
              name="max_load"
              rules={[
                {
                  required: true
                }
              ]}
            >
              <InputNumber min={0} precision={1} suffix="%" />
            </Form.Item>
          </LayoutFormRow>
          <Form.List name={["employee_team_members"]}>
            {(fields, { add, remove, move }) => {
              return (
                <LayoutFormRow
                  title={t("employee_teams.labels.members")}
                  actions={[
                    <Button
                      key="add-team-member"
                      type="primary"
                      block
                      onClick={() => {
                        add({
                          percentage: 0,
                          payout_method: "hourly",
                          labor_rates: {},
                          commission_rates: {}
                        });
                      }}
                    >
                      {t("employee_teams.actions.newmember")}
                    </Button>
                  ]}
                >
                  <div>
                    {fields.length === 0 ? (
                      <ConfigListEmptyState actionLabel={t("employee_teams.actions.newmember")} />
                    ) : (
                      fields.map((field, index) => {
                        return (
                          <Form.Item noStyle key={field.key}>
                            <Form.Item name={[field.name, "id"]} hidden>
                              <Input type="hidden" />
                            </Form.Item>
                            <InlineValidatedFormRow
                              form={teamForm}
                              errorNames={[
                                ["employee_team_members", field.name, "employeeid"],
                                ["employee_team_members", field.name, "percentage"],
                                ["employee_team_members", field.name, "payout_method"]
                              ]}
                              grow
                              title={
                                <div style={INLINE_TITLE_ROW_STYLE}>
                                  <InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
                                  <div style={INLINE_TITLE_GROUP_STYLE}>
                                    <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.employeeid")}</div>
                                    <Form.Item
                                      noStyle
                                      name={[field.name, "employeeid"]}
                                      rules={[
                                        {
                                          required: true
                                        }
                                      ]}
                                    >
                                      <EmployeeSearchSelectComponent options={bodyshop.employees} />
                                    </Form.Item>
                                  </div>
                                  <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
                                  <div style={INLINE_TITLE_GROUP_STYLE}>
                                    <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.allocation")}</div>
                                    <Form.Item
                                      noStyle
                                      name={[field.name, "percentage"]}
                                      rules={[
                                        {
                                          required: true
                                        }
                                      ]}
                                    >
                                      <InputNumber
                                        min={0}
                                        max={100}
                                        precision={2}
                                        size="small"
                                        aria-label={t("employee_teams.fields.allocation")}
                                        suffix="%"
                                        style={{
                                          ...INLINE_TITLE_INPUT_STYLE,
                                          width: "100%"
                                        }}
                                      />
                                    </Form.Item>
                                  </div>
                                  <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
                                  <div style={INLINE_TITLE_GROUP_STYLE}>
                                    <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.payout_method")}</div>
                                    <Form.Item
                                      noStyle
                                      key={`${index}-payout-method`}
                                      name={[field.name, "payout_method"]}
                                      initialValue="hourly"
                                      rules={[
                                        {
                                          required: true
                                        }
                                      ]}
                                    >
                                      <Select
                                        aria-label={t("employee_teams.fields.payout_method")}
                                        size="small"
                                        options={payoutMethodOptions}
                                        style={{ width: "100%" }}
                                        styles={{
                                          selector: INLINE_TITLE_INPUT_STYLE
                                        }}
                                      />
                                    </Form.Item>
                                  </div>
                                </div>
                              }
                              wrapTitle
                              extra={
                                <Space align="center" size="small">
                                  <Button
                                    type="text"
                                    danger
                                    icon={<DeleteFilled />}
                                    onClick={() => {
                                      remove(field.name);
                                    }}
                                  />
                                  <FormListMoveArrows
                                    move={move}
                                    index={index}
                                    total={fields.length}
                                    orientation="horizontal"
                                  />
                                </Space>
                              }
                            >
                              <div>
                                <Form.Item
                                  noStyle
                                  dependencies={[["employee_team_members", field.name, "payout_method"]]}
                                >
                                  {() => {
                                    const payoutMethod =
                                      teamForm.getFieldValue(["employee_team_members", field.name, "payout_method"]) ||
                                      "hourly";
                                    const fieldName = payoutMethod === "commission" ? "commission_rates" : "labor_rates";

                                    return (
                                      <Row gutter={[16, 0]}>
                                        {LABOR_TYPES.map((laborType) => (
                                          <Col
                                            {...TEAM_MEMBER_RATE_FIELD_COLS}
                                            key={`${index}-${fieldName}-${laborType}`}
                                          >
                                            <Form.Item
                                              label={t(`joblines.fields.lbr_types.${laborType}`)}
                                              name={[field.name, fieldName, laborType]}
                                              rules={[
                                                {
                                                  required: true
                                                }
                                              ]}
                                            >
                                              {payoutMethod === "commission" ? (
                                                <InputNumber min={0} max={100} precision={2} suffix="%" />
                                              ) : (
                                                <CurrencyInput prefix="$" />
                                              )}
                                            </Form.Item>
                                          </Col>
                                        ))}
                                      </Row>
                                    );
                                  }}
                                </Form.Item>
                              </div>
                            </InlineValidatedFormRow>
                          </Form.Item>
                        );
                      })
                    )}
                  </div>
                </LayoutFormRow>
              );
            }}
          </Form.List>
        </Form>
      )}
    </Card>
  );
}

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