import Icon, { SyncOutlined } from "@ant-design/icons";
import { cloneDeep } from "lodash";
import { useMutation, useQuery } from "@apollo/client";
import { Button, Dropdown, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import { useMemo, useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { GenerateDashboardData } from "./dashboard-grid.utils";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import componentList from "./componentList.js";
import createDashboardQuery from "./createDashboardQuery.js";

import "./dashboard-grid.styles.scss";

const ResponsiveReactGridLayout = WidthProvider(Responsive);

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

const mapDispatchToProps = () => ({
  //setUserLanguage: language => dispatch(setUserLanguage(language))
});

export function DashboardGridComponent({ currentUser, bodyshop }) {
  const { t } = useTranslation();
  const [state, setState] = useState(() => {
    const persisted = bodyshop.associations[0].user.dashboardlayout;
    // Normalize persisted structure to avoid malformed shapes that can cause recursive layout recalculations
    if (persisted) {
      return {
        items: Array.isArray(persisted.items) ? persisted.items : [],
        layout: Array.isArray(persisted.layout) ? persisted.layout : [],
        layouts: typeof persisted.layouts === "object" && !Array.isArray(persisted.layouts) ? persisted.layouts : {},
        cols: persisted.cols
      };
    }
    return { items: [], layout: [], layouts: {}, cols: 12 };
  });
  const notification = useNotification();

  // Memoize the query document so Apollo doesn't treat each render as a brand-new query causing continuous re-fetches
  const dashboardQueryDoc = useMemo(() => createDashboardQuery(state.items), [state.items]);

  const { loading, error, data, refetch } = useQuery(dashboardQueryDoc, {
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });

  const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);

  const handleLayoutChange = async (layout, layouts) => {
    try {
      logImEXEvent("dashboard_change_layout");

      setState((prev) => ({ ...prev, layout, layouts }));

      const result = await updateLayout({
        variables: {
          email: currentUser.email,
          layout: { ...state, layout, layouts }
        }
      });

      if (result?.errors && result.errors.length) {
        const errorMessages = result.errors.map((e) => e?.message || String(e));
        notification.error({
          message: t("dashboard.errors.updatinglayout", {
            message: errorMessages.join("; ")
          })
        });
      }
    } catch (err) {
      // Catch any unexpected errors (including potential cyclic JSON issues) so the promise never rejects unhandled
      console.error("Dashboard layout update failed", err);
      notification.error({
        message: t("dashboard.errors.updatinglayout", {
          message: err?.message || String(err)
        })
      });
    }
  };

  const handleRemoveComponent = (key) => {
    logImEXEvent("dashboard_remove_component", { name: key });
    const idxToRemove = state.items.findIndex((i) => i.i === key);

    const items = cloneDeep(state.items);

    items.splice(idxToRemove, 1);
    setState({ ...state, items });
  };

  const handleAddComponent = (e) => {
    // Avoid passing the full AntD menu click event (contains circular refs) to analytics
    logImEXEvent("dashboard_add_component", { key: e.key });
    const compSpec = componentList[e.key] || {};
    const minW = compSpec.minW || 1;
    const minH = compSpec.minH || 1;
    const baseW = compSpec.w || 2;
    const baseH = compSpec.h || 2;
    setState((prev) => {
      const nextItems = [
        ...prev.items,
        {
          i: e.key,
          // Position near bottom: use a large y so RGL places it last without triggering cascading relayout loops
          x: (prev.items.length * 2) % (prev.cols || 12),
          y: 1000,
          w: Math.max(baseW, minW),
          h: Math.max(baseH, minH)
        }
      ];
      return { ...prev, items: nextItems };
    });
  };

  const dashboardData = useMemo(() => GenerateDashboardData(data), [data]);

  const existingLayoutKeys = state.items.map((i) => i.i);

  const menuItems = Object.keys(componentList).map((key) => ({
    key: key,
    label: componentList[key].label,
    value: key,
    disabled: existingLayoutKeys.includes(key)
  }));

  const menu = { items: menuItems, onClick: handleAddComponent };

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

  return (
    <div>
      <PageHeader
        extra={
          <Space>
            <Button onClick={() => refetch()}>
              <SyncOutlined />
            </Button>
            <Dropdown menu={menu} trigger={["click"]}>
              <Button>{t("dashboard.actions.addcomponent")}</Button>
            </Dropdown>
          </Space>
        }
      />

      <ResponsiveReactGridLayout
        className="layout"
        breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
        cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
        layouts={state.layouts}
        onLayoutChange={handleLayoutChange}
      >
        {state.items.map((item) => {
          const spec = componentList[item.i] || {};
          const TheComponent = spec.component;
          const minW = spec.minW || 1;
          const minH = spec.minH || 1;
          // Ensure current width/height respect minimums to avoid react-grid-layout prop warnings
          const safeItem = {
            ...item,
            w: Math.max(item.w || spec.w || minW, minW),
            h: Math.max(item.h || spec.h || minH, minH)
          };
          return (
            <div
              key={safeItem.i}
              data-grid={{
                ...safeItem,
                minH,
                minW
              }}
            >
              <LoadingSkeleton loading={loading}>
                <Icon
                  component={MdClose}
                  key={safeItem.i}
                  style={{
                    position: "absolute",
                    zIndex: "2",
                    right: ".25rem",
                    top: ".25rem",
                    cursor: "pointer"
                  }}
                  onClick={() => handleRemoveComponent(safeItem.i)}
                />
                {TheComponent && <TheComponent className="dashboard-card" bodyshop={bodyshop} data={dashboardData} />}
              </LoadingSkeleton>
            </div>
          );
        })}
      </ResponsiveReactGridLayout>
    </div>
  );
}

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