import { useAbly, useChannel } from 'ably/react';
import { useCallback, useEffect, useMemo } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { ProjectMetadataSchema } from '@/state/projectStore';

export type WorkspaceChangeAction =
  | 'add_clips'
  | 'remove_clips'
  | 'move_clips'
  | 'pin_clips'
  | 'unpin_clips';

// Global service that can be accessed from anywhere
class WorkspaceCollaborationService {
  private makeWorkspaceChangeFn:
    | ((action: WorkspaceChangeAction) => boolean)
    | null = null;
  private kickUserFromWorkspaceFn: ((userId: string) => void) | null = null;

  setMakeWorkspaceChangeFn(fn: (action: WorkspaceChangeAction) => boolean) {
    this.makeWorkspaceChangeFn = fn;
  }

  makeWorkspaceChange(action: WorkspaceChangeAction) {
    if (this.makeWorkspaceChangeFn) {
      return this.makeWorkspaceChangeFn(action);
    }
    return false;
  }

  setKickUserFromWorkspaceFn(fn: (userId: string) => void) {
    this.kickUserFromWorkspaceFn = fn;
  }

  kickUserFromWorkspace(userId: string) {
    if (this.kickUserFromWorkspaceFn) {
      this.kickUserFromWorkspaceFn(userId);
    }
  }

  clear() {
    this.makeWorkspaceChangeFn = null;
    this.kickUserFromWorkspaceFn = null;
  }
}

// Export singleton instance
export const workspaceCollaborationService =
  new WorkspaceCollaborationService();

interface WorkspaceCollaborationInitializerProps {
  project: any;
}

export const WorkspaceCollaborationInitializer = ({
  project,
}: WorkspaceCollaborationInitializerProps) => {
  const ablyClient = useAbly();
  const { session } = useStores();

  const channelName = useMemo(
    () => `suno-collab-workspace:${project.currentProjectId}`,
    [project.currentProjectId]
  );

  const { publish } = useChannel(channelName);

  const publishWorkspaceChange = useCallback(
    (action: WorkspaceChangeAction) => {
      if (
        ablyClient &&
        (
          project.projectsById[
            project.currentProjectId
          ] as ProjectMetadataSchema
        )?.shared &&
        project.root.session.user?.id
      ) {
        const message = {
          type: 'workspace_change',
          projectId: project.currentProjectId,
          action,
          userId: project.root.session.user.id,
          timestamp: Date.now(),
        };
        publish({
          name: 'workspace_change',
          data: JSON.stringify(message),
        });
      }
    },
    [
      ablyClient,
      (project.projectsById[project.currentProjectId] as ProjectMetadataSchema)
        ?.shared,
      project.currentProjectId,
      project.root.session.user?.id,
      publish,
    ]
  );

  const makeWorkspaceChange = useCallback(
    (action: WorkspaceChangeAction) => {
      if (
        session.flags?.['collab-workspaces'] &&
        ablyClient &&
        (
          project.projectsById[
            project.currentProjectId
          ] as ProjectMetadataSchema
        )?.shared
      ) {
        publishWorkspaceChange(action);
        return true;
      }
      return false;
    },
    [
      ablyClient,
      (project.projectsById[project.currentProjectId] as ProjectMetadataSchema)
        ?.shared,
      publishWorkspaceChange,
      session,
    ]
  );

  // Set up the global service when the initializer mounts
  useEffect(() => {
    workspaceCollaborationService.setMakeWorkspaceChangeFn(makeWorkspaceChange);
  }, [makeWorkspaceChange]);

  const kickUserFromWorkspace = useCallback(
    (userId: string) => {
      if (
        session.flags?.['collab-workspaces'] &&
        ablyClient &&
        (
          project.projectsById[
            project.currentProjectId
          ] as ProjectMetadataSchema
        )?.shared
      ) {
        const message = {
          type: 'kick_user',
          projectId: project.currentProjectId,
          userId,
          timestamp: Date.now(),
        };
        publish({
          name: 'kick_user',
          data: JSON.stringify(message),
        });
      }
    },
    [
      ablyClient,
      (project.projectsById[project.currentProjectId] as ProjectMetadataSchema)
        ?.shared,
      project.currentProjectId,
      publish,
      session,
    ]
  );
  useEffect(() => {
    workspaceCollaborationService.setKickUserFromWorkspaceFn(
      kickUserFromWorkspace
    );
  }, [kickUserFromWorkspace]);

  // Clean up when the initializer unmounts
  useEffect(() => {
    return () => {
      workspaceCollaborationService.clear();
    };
  }, []);

  // This component doesn't render anything
  return null;
};
