import React, {
  createContext,
  useContext,
  useCallback,
  useRef,
  useState,
  useEffect,
} from "react";
import { useWebSocket } from "../hooks/useWebSocket";
import { toast } from "react-toastify";
import { getRowId } from "../utils/rowUtils";

const RealtimeContext = createContext(null);

export const useRealtime = () => {
  const context = useContext(RealtimeContext);
  if (!context) {
    throw new Error("useRealtime must be used within RealtimeProvider");
  }
  return context;
};

export const RealtimeProvider = ({
  children,
  formId,
  sheetName,
  direction,
  year,
  userId,
  isProject = false,
}) => {
  // Формируем URL (кастомные секреты на фронте пока недоступны, делаем через REACT_APP_ROOT_PATH)
  const wsUrl =
    `${(
      process.env.REACT_APP_API_URL ||
      process.env.REACT_APP_API_URL ||
      process.env.REACT_APP_ROOT_PATH ||
      "ws://localhost:8000"
    ).replace(/^http/, "ws")}` +
    (!isProject
      ? `/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`
      : `/api/v1/ws/projects/${formId}/report/${year}/${sheetName}`);

  //для ячеек которые редактируются другими пользователями
  const [lockedCells, setLockedCells] = useState([]);
  const handleMessage = useCallback((data) => {
    // Обработка ошибок
    if (data.error) {
      console.log(data.error.message);
      toast.error("Server error:" + data.error.message || "");
      onErrorRef.current?.(data.error);
      return;
    }

    switch (data.event) {
      case "cell_edit_start":
        console.log("Cell edit started:", data);
        if (onCellEditStartRef.current) {
          onCellEditStartRef.current(data);
        }
        break;

      case "cell_edit_end":
        console.log("Cell edit ended:", data);
        if (onCellEditEndRef.current) {
          onCellEditEndRef.current(data);
        }
        break;

      case "cell_updated":
        if (data.result && onCellUpdateRef.current) {
          const updatedCells = Array.isArray(data.result)
            ? data.result
            : [data.result];
          onCellUpdateRef.current(updatedCells);
        }
        break;

      case "row_added":
        if (data.result && onRowAddRef.current) {
          onRowAddRef.current(data);
        }
        break;

      case "row_deleted":
        if (data.result && onRowDeleteRef.current) {
          onRowDeleteRef.current(data);
        }
        break;

      case "program_added":
        console.log("Program added:", data);
        if (data.result && onProgramAddRef.current) {
          onProgramAddRef.current(data);
        }
        break;

      case "project_added":
        console.log("Project added:", data);
        if (data.result && onProjectAddRef.current) {
          onProjectAddRef.current(data);
        }
        break;

      default:
        console.log("Unknown event:", data.event);
    }
  }, []);

  const { isConnectedRef, isConnected, sendMessage, disconnect, lastError } =
    useWebSocket(wsUrl, handleMessage);

  const onCellUpdateRef = useRef(null);
  const onRowAddRef = useRef(null);
  const onRowDeleteRef = useRef(null);
  const onProgramAddRef = useRef(null);
  const onProjectAddRef = useRef(null);
  const onErrorRef = useRef(null);
  const onAuthSuccessRef = useRef(null);
  const onCellEditStartRef = useRef(null);
  const onCellEditEndRef = useRef(null);
  const authAttemptedRef = useRef(false);
  const reconnectTimerRef = useRef(null);

  // Функция для получения токена из localStorage
  const getAccessToken = useCallback(() => {
    try {
      const token = localStorage.getItem("access_token");

      if (!token) {
        console.warn("No access token found in localStorage");
        return null;
      }

      return token;
    } catch (error) {
      console.error("Error reading token from localStorage:", error);
      return null;
    }
  }, []);

  const sendLogin = useCallback(() => {
    const token = getAccessToken();
    if (!token) {
      console.error("Cannot login: no token available");
      onErrorRef.current?.("Токен авторизации не найден");
      return false;
    }

    const loginMessage = {
      event: "user_login",
      data: { token },
    };

    return sendMessage(loginMessage);
  }, [getAccessToken, sendMessage]);

  useEffect(() => {
    if (isConnected && !authAttemptedRef.current) {
      authAttemptedRef.current = true;
      console.log("WebSocket connected, sending login...");
      setTimeout(() => {
        sendLogin();
      }, 100);
    }
  }, [isConnected, sendLogin]);

  useEffect(() => {
    if (!isConnected) {
      authAttemptedRef.current = false;
    }
  }, [isConnected]);

  const addCommonField = (message) => {
    if (isProject) {
      message.report_type = sheetName;
      message.project_id = formId;
      message.year = year;
    } else {
      message.sheet = sheetName;
      message.form_id = formId;
      message.direction = direction;
    }
    return message;
  };

  // Функция для начала редактирования ячейки
  const startEditing = useCallback(
    async (row, column) => {
      const columnId = column.id;
      const rowId = row.id;

      if (!isConnectedRef) {
        onErrorRef.current?.("WebSocket не подключен");
        return false;
      }

      const lineId = row.id || null;
      const colId = columnId.slice(5); // убираем префикс "data."

      const message = {
        event: "cell_edit_start",
        data: {
          line_id: lineId,
          column: colId,
        },
      };

      const sent = sendMessage(message);
      if (!sent) {
        return false;
      }

      return true;
    },
    [isConnected, sendMessage],
  );

  // Функция для завершения редактирования ячейки
  const endEditing = useCallback(
    async (row, column) => {
      const columnId = column.id;
      const rowId = row.id;

      if (!isConnectedRef) {
        onErrorRef.current?.("WebSocket не подключен");
        return false;
      }

      const lineId = row.id || null;
      const colId = columnId.slice(5);

      const message = {
        event: "cell_edit_end",
        data: {
          line_id: lineId,
          column: colId,
        },
      };

      const sent = sendMessage(message);
      if (!sent) {
        return false;
      }

      return true;
    },
    [isConnected, sendMessage],
  );

  const updateCell = useCallback(
    async (row, column, value) => {
      const columnId = column.id;
      const rowId = row.id;
      if (!isConnectedRef) {
        onErrorRef.current?.("WebSocket не подключен");
        return false;
      }

      const lineId = getRowId(row);
      const colId = columnId.slice(5);
      const message = {
        event: "cell_updated",
        data: {
          line_id: lineId,
          line_id_code: row.id,
          column: colId,
          value: value,
        },
      };
      const sent = sendMessage(message);

      if (!sent) {
        return false;
      }

      return true;
    },
    [isConnected, sendMessage],
  );

  const addRow = useCallback(
    async (rowData) => {
      if (!isConnectedRef) {
        onErrorRef.current?.("Нет подключения или авторизации");
        return false;
      }

      const message = {
        event: "row_added",
        data: rowData,
      };
      addCommonField(message);

      return sendMessage(message);
    },
    [isConnected, sendMessage, sheetName, formId, direction],
  );

  const addProject = useCallback(
    async (rowData) => {
      if (!isConnectedRef) {
        onErrorRef.current?.("Нет подключения или авторизации");
        return false;
      }

      const message = {
        event: "project_added",
        data: rowData,
      };
      addCommonField(message);

      return sendMessage(message);
    },
    [isConnected, sendMessage, sheetName, formId, direction],
  );

  const addProgram = useCallback(
    async (rowData) => {
      if (!isConnectedRef) {
        onErrorRef.current?.("Нет подключения или авторизации");
        return false;
      }

      const message = {
        event: "program_added",
        data: rowData,
      };
      addCommonField(message);

      return sendMessage(message);
    },
    [isConnected, sendMessage, sheetName, formId, direction],
  );

  const deleteRow = useCallback(
    async (rowId) => {
      if (!isConnectedRef) {
        onErrorRef.current?.("Нет подключения или авторизации");
        return false;
      }

      const message = {
        event: "row_deleted",
        data: { row_id: rowId },
      };
      addCommonField(message);

      return sendMessage(message);
    },
    [isConnected, sendMessage, sheetName, formId, direction],
  );

  const subscribeToCellUpdates = useCallback((callback) => {
    onCellUpdateRef.current = callback;
    return () => {
      onCellUpdateRef.current = null;
    };
  }, []);

  const subscribeToRowAdds = useCallback((callback) => {
    onRowAddRef.current = callback;
    return () => {
      onRowAddRef.current = null;
    };
  }, []);

  const subscribeToRowDeletes = useCallback((callback) => {
    onRowDeleteRef.current = callback;
    return () => {
      onRowDeleteRef.current = null;
    };
  }, []);

  const subscribeToProgramAdds = useCallback((callback) => {
    onProgramAddRef.current = callback;
    return () => {
      onProgramAddRef.current = null;
    };
  }, []);

  const subscribeToProjectAdds = useCallback((callback) => {
    onProjectAddRef.current = callback;
    return () => {
      onProjectAddRef.current = null;
    };
  }, []);

  const subscribeToErrors = useCallback((callback) => {
    onErrorRef.current = callback;
    return () => {
      onErrorRef.current = null;
    };
  }, []);

  const subscribeToAuthSuccess = useCallback((callback) => {
    onAuthSuccessRef.current = callback;
    return () => {
      onAuthSuccessRef.current = null;
    };
  }, []);

  const subscribeToCellEditStart = useCallback((callback) => {
    onCellEditStartRef.current = callback;
    return () => {
      onCellEditStartRef.current = null;
    };
  }, []);

  const subscribeToCellEditEnd = useCallback((callback) => {
    onCellEditEndRef.current = callback;
    return () => {
      onCellEditEndRef.current = null;
    };
  }, []);

  const retryLogin = useCallback(() => {
    if (isConnected) {
      authAttemptedRef.current = false;
      sendLogin();
    }
  }, [isConnected, sendLogin]);

  return (
    <RealtimeContext.Provider
      value={{
        isConnected,
        error: lastError,
        startEditing,
        endEditing,
        updateCell,
        addRow,
        addProgram,
        addProject,
        deleteRow,
        subscribeToRowAdds,
        subscribeToRowDeletes,
        subscribeToProgramAdds,
        subscribeToProjectAdds,
        subscribeToErrors,
        subscribeToAuthSuccess,
        subscribeToCellEditStart,
        subscribeToCellEditEnd,
        subscribeToCellUpdates,
        retryLogin,
        lockedCells,
        setLockedCells,
        releaseAllLocks: () => releaseAllLocks(userId),
      }}
    >
      {children}
    </RealtimeContext.Provider>
  );
};
