import { useEffect, useRef } from "react";
import { useApolloClient, useQuery, useSubscription } from "@apollo/client/react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
  QUERY_EXACT_JOB_IN_PRODUCTION,
  QUERY_JOBS_IN_PRODUCTION,
  SUBSCRIPTION_JOBS_IN_PRODUCTION,
  SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW
} from "../../graphql/jobs.queries";
import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import ProductionBoardKanbanComponent from "./production-board-kanban.component";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { useSocket } from "../../contexts/SocketIO/useSocket.js";

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

function ProductionBoardKanbanContainer({ bodyshop, currentUser, subscriptionType = "direct" }) {
  const fired = useRef(false);
  const client = useApolloClient();
  const { socket } = useSocket();
  const reconnectTimeout = useRef(null); // To store the reconnect timeout
  const disconnectTime = useRef(null); // To track disconnection time
  const acceptableReconnectTime = 2000; // 2 seconds threshold

  const {
    treatments: { Websocket_Production }
  } = useTreatmentsWithConfig({
    attributes: {},
    names: ["Websocket_Production"],
    splitKey: bodyshop && bodyshop.imexshopid
  });

  const combinedStatuses = [
    ...bodyshop.md_ro_statuses.production_statuses,
    ...(bodyshop.md_ro_statuses.additional_board_statuses || [])
  ];

  const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
    pollInterval: 3600000,
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only",
    onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`)
  });

  const subscriptionEnabled = Websocket_Production?.treatment === "off";

  const { data: updatedJobs } = useSubscription(
    subscriptionType === "view" ? SUBSCRIPTION_JOBS_IN_PRODUCTION_VIEW : SUBSCRIPTION_JOBS_IN_PRODUCTION,
    {
      skip: !subscriptionEnabled,
      onError: (error) => console.error(`Error subscribing to jobs in production: ${error.message}`)
    }
  );

  const { loading: associationSettingsLoading, data: associationSettings } = useQuery(QUERY_KANBAN_SETTINGS, {
    variables: { email: currentUser.email },
    onError: (error) => console.error(`Error fetching Kanban settings: ${error.message}`)
  });

  useEffect(() => {
    if (subscriptionEnabled) {
      if (!updatedJobs) {
        return;
      }
      if (!fired.current) {
        fired.current = true;
        return;
      }
      refetch().catch((err) => console.error(`Error re-fetching jobs in production: ${err.message}`));
    }
  }, [updatedJobs, refetch, subscriptionEnabled]);

  // Socket.IO implementation for users with Split treatment "off"
  useEffect(() => {
    if (subscriptionEnabled || !socket || !bodyshop || !bodyshop.id) {
      return;
    }

    const handleJobUpdates = async (jobChangedData) => {
      const jobId = jobChangedData.id;

      // Access the existing cache for QUERY_JOBS_IN_PRODUCTION
      const existingJobsCache = client.readQuery({
        query: QUERY_JOBS_IN_PRODUCTION
      });

      const existingJobs = existingJobsCache?.jobs || [];

      // Check if the job already exists in the cached jobs
      const existingJob = existingJobs.find((job) => job.id === jobId);

      if (existingJob) {
        // If the job exists, we update the cache without making any additional queries
        client.writeQuery({
          query: QUERY_JOBS_IN_PRODUCTION,
          data: {
            jobs: existingJobs.map((job) =>
              job.id === jobId ? { ...existingJob, ...jobChangedData, __typename: "jobs" } : job
            )
          }
        });
      } else {
        // If the job doesn't exist, fetch it from the server and then add it to the cache
        try {
          const { data: jobData } = await client.query({
            query: QUERY_EXACT_JOB_IN_PRODUCTION,
            variables: { id: jobId },
            fetchPolicy: "network-only"
          });

          // Add the job to the existing cached jobs
          client.writeQuery({
            query: QUERY_JOBS_IN_PRODUCTION,
            data: {
              jobs: [...existingJobs, { ...jobData.job, __typename: "jobs" }]
            }
          });
        } catch (error) {
          console.error(`Error fetching job ${jobId}: ${error.message}`);
        }
      }
    };

    const handleDisconnect = () => {
      // Capture the disconnection time
      disconnectTime.current = Date.now();
    };

    const handleReconnect = () => {
      const reconnectTime = Date.now();
      const disconnectionDuration = reconnectTime - disconnectTime.current;

      // Only refetch if disconnection was longer than the acceptable reconnect time
      if (disconnectionDuration >= acceptableReconnectTime) {
        if (!reconnectTimeout.current) {
          reconnectTimeout.current = setTimeout(() => {
            const randomDelay = Math.floor(Math.random() * (30000 - 10000 + 1)) + 10000; // Random delay between 10 and 30 seconds
            setTimeout(() => {
              if (refetch) refetch().catch(() => console.error(`Issue `));
              reconnectTimeout.current = null; // Clear the timeout reference after refetch
            }, randomDelay);
          }, acceptableReconnectTime);
        }
      }
    };

    // Listen for 'job-changed', 'disconnect', and 'connect' events
    socket.on("production-job-updated", handleJobUpdates);
    socket.on("disconnect", handleDisconnect);
    socket.on("connect", handleReconnect);

    // Clean up on unmount or when dependencies change
    return () => {
      socket.off("production-job-updated", handleJobUpdates);
      socket.off("disconnect", handleDisconnect);
      socket.off("connect", handleReconnect);
      if (reconnectTimeout.current) {
        clearTimeout(reconnectTimeout.current);
      }
    };
  }, [subscriptionEnabled, socket, bodyshop, client, refetch]);

  const filteredAssociationSettings = associationSettings?.associations[0] || null;

  return (
    <ProductionBoardKanbanComponent
      loading={loading || associationSettingsLoading}
      data={data ? data.jobs : []}
      refetch={refetch}
      associationSettings={filteredAssociationSettings}
      bodyshop={bodyshop}
      statuses={combinedStatuses}
    />
  );
}

export default connect(mapStateToProps)(ProductionBoardKanbanContainer);
