import {StateCreator} from 'zustand';
import {
  PlayerState,
  ProviderListeners,
} from '@common/player/state/player-state';
import {PipAdapter} from '@common/player/state/pip/pip-adapter';
import {createChromePipAdapter} from '@common/player/state/pip/chrome-pip-adapter';
import {createSafariPipAdapter} from '@common/player/state/pip/safari-pip-adapter';

export interface PipSlice {
  isPip: boolean;
  canPip: boolean;
  enterPip: () => void;
  exitPip: () => void;
  togglePip: () => void;
  initPip: () => void;
  destroyPip: () => void;
}

type BaseSliceCreator = StateCreator<
  PipSlice & PlayerState,
  [['zustand/immer', unknown]],
  [],
  PipSlice
>;

type StoreLice = BaseSliceCreator extends (...a: infer U) => infer R
  ? (...a: [...U, Set<Partial<ProviderListeners>>]) => R
  : never;

const adapterFactories = [createChromePipAdapter, createSafariPipAdapter];

export const createPipSlice: StoreLice = (set, get) => {
  let subscription: () => void | undefined;
  let adapters: PipAdapter[] = [];

  const onPipChange = () => {
    set({isPip: adapters.some(a => a.isPip())});
  };

  const isSupported = (): boolean => {
    if (get().providerName !== 'htmlVideo') {
      return false;
    }
    return adapters.some(adapter => adapter.isSupported());
  };

  return {
    isPip: false,
    canPip: false,
    enterPip: async () => {
      if (get().isPip || !isSupported()) return;
      await adapters.find(a => a.isSupported())?.enter();
    },
    exitPip: async () => {
      if (!get().isPip) return;
      await adapters.find(a => a.isSupported())?.exit();
    },
    togglePip: () => {
      if (get().isPip) {
        get().exitPip();
      } else {
        get().enterPip();
      }
    },
    initPip: () => {
      subscription = get().subscribe({
        providerReady: ({el}) => {
          // when changing adapters, remove previous adapter events and exit pip
          adapters.every(a => a.unbindEvents());
          if (get().isPip) {
            adapters.every(a => a.exit());
          }
          // create new adapters, and if pip is supported on at least one, bind events
          adapters = adapterFactories.map(factory =>
            factory(el as HTMLVideoElement, onPipChange)
          );
          const canPip = isSupported();
          if (canPip) {
            adapters.every(a => a.bindEvents());
          }
          set({canPip});
        },
      });
    },
    destroyPip: () => {
      get().exitPip();
      subscription?.();
    },
  };
};
