import { useCallback, useEffect, useMemo, useState } from "react";
import { useMutation, useQuery } from "@apollo/client/react";
import { ADD_JOB_WATCHER, GET_JOB_WATCHERS, REMOVE_JOB_WATCHER } from "../../graphql/jobs.queries.js";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import JobWatcherToggleComponent from "./job-watcher-toggle.component.jsx";
import { useIsEmployee } from "../../utils/useIsEmployee.js";

const EMPTY_ARRAY = Object.freeze([]);

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

function JobWatcherToggleContainer({ job, currentUser, bodyshop }) {
  const {
    treatments: { Enhanced_Payroll }
  } = useTreatmentsWithConfig({
    attributes: {},
    names: ["Enhanced_Payroll"],
    splitKey: bodyshop && bodyshop.imexshopid
  });

  const isEmployee = useIsEmployee(bodyshop, currentUser);
  const [open, setOpen] = useState(false);
  const [selectedWatcher, setSelectedWatcher] = useState(null);
  const [selectedTeam, setSelectedTeam] = useState(null);

  const userEmail = currentUser?.email;
  const jobid = job?.id;
  const watcherVars = useMemo(() => ({ jobid }), [jobid]);

  const {
    data: watcherData,
    loading: watcherLoading,
    refetch
  } = useQuery(GET_JOB_WATCHERS, {
    variables: watcherVars,
    skip: !jobid,
    fetchPolicy: "cache-first",
    notifiyOnNetworkStatusChange: true
  });

  // Refetch jobWatchers when the popover opens
  useEffect(() => {
    if (!jobid) return;
    if (open) {
      refetch().catch((err) =>
        console.error(`Something went wrong fetching Notification Watchers on popover open: ${err?.message}`, {
          stack: err?.stack
        })
      );
    }
  }, [open, refetch, jobid]);

  // Do NOT clone arrays; keep referential stability for React Compiler and to reduce rerenders.
  const jobWatchers = watcherData?.job_watchers ?? EMPTY_ARRAY;
  const watcherEmailSet = useMemo(
    () => new Set((jobWatchers ?? EMPTY_ARRAY).map((w) => w?.user_email).filter(Boolean)),
    [jobWatchers]
  );
  const isWatching = !!userEmail && watcherEmailSet.has(userEmail);

  const [addWatcher, { loading: adding }] = useMutation(ADD_JOB_WATCHER, {
    onError: (err) => {
      if (err.graphQLErrors && err.graphQLErrors.length > 0) {
        const errorMessage = err.graphQLErrors[0].message;
        if (
          errorMessage.includes("Uniqueness violation") ||
          errorMessage.includes("idx_job_watchers_jobid_user_email_unique")
        ) {
          console.warn("Watcher already exists for this job and user.");
          // Only refetch for this edge case to ensure UI is accurate
          refetch().catch((e) =>
            console.error(
              `Something went wrong fetching Notification Watchers after uniqueness violation: ${e?.message}`,
              { stack: e?.stack }
            )
          );
        } else {
          console.error(`Error adding job watcher: ${errorMessage}`);
        }
      } else {
        console.error(`Unexpected error adding job watcher: ${err.message || JSON.stringify(err)}`);
      }
    },
    update(cache, { data }) {
      const inserted = data?.insert_job_watchers_one;
      if (!inserted) return;

      cache.updateQuery({ query: GET_JOB_WATCHERS, variables: watcherVars }, (existing) => {
        const prev = existing?.job_watchers ?? [];
        if (prev.some((w) => w.user_email === inserted.user_email)) return existing;

        return {
          ...existing,
          job_watchers: [...prev, inserted]
        };
      });
    }
  });

  const [removeWatcher, { loading: removing }] = useMutation(REMOVE_JOB_WATCHER, {
    onError: (err) => console.error(`Error removing job watcher: ${err.message}`),
    update(cache, { data }) {
      const deleted = data?.delete_job_watchers?.returning?.[0];
      if (!deleted?.user_email) return;

      cache.updateQuery({ query: GET_JOB_WATCHERS, variables: watcherVars }, (existing) => {
        const prev = existing?.job_watchers ?? [];
        return {
          ...existing,
          job_watchers: prev.filter((w) => w.user_email !== deleted.user_email)
        };
      });
    }
  });

  const handleToggleSelf = useCallback(async () => {
    if (!jobid || !userEmail) return;
    if (adding || removing || !isEmployee) return;

    if (isWatching) {
      await removeWatcher({ variables: { jobid, userEmail } });
    } else {
      await addWatcher({ variables: { jobid, userEmail } });
    }
  }, [isWatching, addWatcher, removeWatcher, jobid, userEmail, adding, removing, isEmployee]);

  const handleRemoveWatcher = useCallback(
    async (email) => {
      if (!jobid) return;
      if (removing) return;
      if (!email) return;
      await removeWatcher({ variables: { jobid, userEmail: email } });
    },
    [removeWatcher, jobid, removing]
  );

  const handleWatcherSelect = useCallback(
    async (selectedUser) => {
      if (!jobid) return;
      if (adding || removing) return;

      const employee = bodyshop?.employees?.find((e) => e.id === selectedUser);
      if (!employee?.user_email) return;

      const email = employee.user_email;
      const isAlreadyWatching = watcherEmailSet.has(email);

      if (isAlreadyWatching) {
        await handleRemoveWatcher(email);
      } else {
        await addWatcher({ variables: { jobid, userEmail: email } });
      }

      setSelectedWatcher(null);
    },
    [watcherEmailSet, addWatcher, handleRemoveWatcher, jobid, bodyshop, adding, removing]
  );

  const handleTeamSelect = useCallback(
    async (team) => {
      if (!jobid) return;
      if (adding) return;

      let selectedTeamMembers = [];
      try {
        selectedTeamMembers = Array.isArray(team) ? team : JSON.parse(team);
      } catch {
        selectedTeamMembers = [];
      }

      const newWatchers = (selectedTeamMembers ?? []).filter(Boolean).filter((email) => !watcherEmailSet.has(email));
      if (newWatchers.length === 0) {
        console.warn("All selected team members are already watchers.");
        setSelectedTeam(null);
        return;
      }

      await Promise.all(
        newWatchers.map((email) =>
          addWatcher({
            variables: {
              jobid,
              userEmail: email
            }
          })
        )
      );

      setSelectedTeam(null);
    },
    [watcherEmailSet, addWatcher, jobid, adding]
  );

  return (
    <JobWatcherToggleComponent
      jobWatchers={jobWatchers}
      isWatching={isWatching}
      watcherLoading={watcherLoading}
      adding={adding}
      removing={removing}
      open={open}
      setOpen={setOpen}
      selectedWatcher={selectedWatcher}
      setSelectedWatcher={setSelectedWatcher}
      selectedTeam={selectedTeam}
      setSelectedTeam={setSelectedTeam}
      bodyshop={bodyshop}
      Enhanced_Payroll={Enhanced_Payroll}
      handleToggleSelf={handleToggleSelf}
      handleRemoveWatcher={handleRemoveWatcher}
      handleWatcherSelect={handleWatcherSelect}
      handleTeamSelect={handleTeamSelect}
      currentUser={currentUser}
      isEmployee={isEmployee}
    />
  );
}

export default connect(mapStateToProps)(JobWatcherToggleContainer);
