import Icon, { SyncOutlined } from "@ant-design/icons";
import { useMutation, useQuery } from "@apollo/client/react";
import { Button, Dropdown, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import { useEffect, useMemo, useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout/legacy";
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 { QUERY_USER_DASHBOARD_LAYOUT, UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import { QUERY_DASHBOARD_BODYSHOP } from "../../graphql/bodyshop.queries";
import { 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
});

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

export function DashboardGridComponent({ currentUser }) {
  const { t } = useTranslation();
  const notification = useNotification();

  // Constants for layout defaults
  const DEFAULT_COLS = 12;
  const DEFAULT_Y_POSITION = 1000;
  const GRID_BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 };
  const GRID_COLS = { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 };

  // Fetch dashboard layout data
  const { data: layoutData } = useQuery(QUERY_USER_DASHBOARD_LAYOUT, {
    variables: { email: currentUser.email },
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only",
    skip: !currentUser?.email
  });

  // Fetch minimal bodyshop data for components
  const {
    loading,
    error,
    data: bodyshopData
  } = useQuery(QUERY_DASHBOARD_BODYSHOP, {
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });

  const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);

  // Memoize layout state initialization
  const initialState = useMemo(() => {
    const persisted = layoutData?.users?.[0]?.dashboardlayout;
    if (persisted) {
      const { items = [], layout = [], layouts = {}, cols = DEFAULT_COLS } = persisted;
      return {
        items: Array.isArray(items) ? items : [],
        layout: Array.isArray(layout) ? layout : [],
        layouts: typeof layouts === "object" && !Array.isArray(layouts) ? layouts : {},
        cols
      };
    }
    return { items: [], layout: [], layouts: {}, cols: DEFAULT_COLS };
  }, [layoutData]);

  const [state, setState] = useState(initialState);

  // Update state when layout data changes
  useEffect(() => {
    if (layoutData?.users?.[0]?.dashboardlayout) {
      const { items = [], layout = [], layouts = {}, cols = DEFAULT_COLS } = layoutData.users[0].dashboardlayout;
      setState({
        items: Array.isArray(items) ? items : [],
        layout: Array.isArray(layout) ? layout : [],
        layouts: typeof layouts === "object" && !Array.isArray(layouts) ? layouts : {},
        cols
      });
    }
  }, [layoutData]);

  // Get bodyshop data for components
  const bodyshop = bodyshopData?.dashboard_bodyshops?.[0];

  // DRY helper function to update layout in database and cache
  const updateLayoutAndCache = async (updatedLayout, errorContext = "updating layout") => {
    try {
      const { data: result } = await updateLayout({
        variables: { email: currentUser.email, layout: updatedLayout }
      });

      const { errors = [] } = result?.update_users?.returning?.[0] || {};

      if (errors.length) {
        const errorMessages = errors.map(({ message }) => message || String(error));
        notification.error({
          title: t("dashboard.errors.updatinglayout", {
            message: errorMessages.join("; ")
          })
        });
        return false;
      }

      return true;
    } catch (err) {
      console.error(`Dashboard ${errorContext} failed`, err);
      notification.error({
        title: t("dashboard.errors.updatinglayout", {
          message: err?.message || String(err)
        })
      });
      return false;
    }
  };

  // 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: dashboardLoading,
    error: dashboardError,
    data: dashboardQueryData,
    refetch
  } = useQuery(dashboardQueryDoc, {
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });

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

  // Memoize existing layout keys to prevent unnecessary recalculations
  const existingLayoutKeys = useMemo(() => state.items.map(({ i }) => i), [state.items]);

  // Memoize menu items to prevent unnecessary recalculations
  const menuItems = useMemo(
    () =>
      Object.entries(componentList).map(([key, { label }]) => ({
        key,
        label,
        value: key,
        disabled: existingLayoutKeys.includes(key)
      })),
    [existingLayoutKeys]
  );

  if (loading || dashboardLoading) return <LoadingSkeleton message={t("general.labels.loading")} />;
  if (error || dashboardError) return <AlertComponent title={(error || dashboardError).message} type="error" />;

  const handleLayoutChange = async (layout, layouts) => {
    logImEXEvent("dashboard_change_layout");
    setState((prev) => ({ ...prev, layout, layouts }));
    await updateLayoutAndCache({ ...state, layout, layouts }, "layout change");
  };

  const handleRemoveComponent = async (key) => {
    logImEXEvent("dashboard_remove_component", { name: key });
    const updatedState = { ...state, items: state.items.filter((item) => item.i !== key) };
    setState(updatedState);
    await updateLayoutAndCache(updatedState, "component removal");
  };

  const handleAddComponent = async ({ key }) => {
    logImEXEvent("dashboard_add_component", { key });
    const { minW = 1, minH = 1, w: baseW = 2, h: baseH = 2 } = componentList[key] || {};
    const nextItems = [
      ...state.items,
      {
        i: key,
        x: (state.items.length * 2) % (state.cols || DEFAULT_COLS),
        y: DEFAULT_Y_POSITION,
        w: Math.max(baseW, minW),
        h: Math.max(baseH, minH)
      }
    ];
    const updatedState = { ...state, items: nextItems };
    setState(updatedState);
    await updateLayoutAndCache(updatedState, "component addition");
  };

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

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

      <ResponsiveReactGridLayout
        className="layout"
        breakpoints={GRID_BREAKPOINTS}
        cols={GRID_COLS}
        layouts={state.layouts}
        onLayoutChange={handleLayoutChange}
      >
        {state.items.map((item) => {
          const { component: TheComponent, minW = 1, minH = 1, w: specW, h: specH } = componentList[item.i] || {};
          const safeItem = {
            ...item,
            w: Math.max(item.w || specW || minW, minW),
            h: Math.max(item.h || specH || 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);
