import type { Track } from "@applets/core/types.js";
import type { Item, State } from "./types";
import { arrayShuffle, expose, transfer } from "@scripts/common.ts";

////////////////////////////////////////////
// STATE
////////////////////////////////////////////

const QUEUE_SIZE = 25;

const internal: { pool: Track[] } = {
  pool: [],
};

////////////////////////////////////////////
// ACTIONS
////////////////////////////////////////////
const actions = expose({
  add,
  pool,
  shift,
  unshift,
});

export type Actions = typeof actions;

// Actions

function add(state: State, items: Item[]): State {
  return transfer({ ...state, future: [...state.future, ...items] });
}

function pool(state: State, tracks: Track[]): State {
  internal.pool = tracks;

  // TODO: If the pool changes, only remove non-existing tracks
  //       instead of resetting the whole future queue.
  //
  //       What about past queue items?

  state = fill({ ...state, future: [] });

  // Automatically insert track if there isn't any
  if (!state.now) return shift(state);
  return transfer(state);
}

function shift(state: State): State {
  const now = state.future[0] || null;
  const future = state.future.slice(1);
  const past = state.now ? [...state.past, state.now] : state.past;
  const filled = fill({ past, now, future });

  return transfer(filled);
}

function unshift(state: State): State {
  if (state.past.length === 0) return state;

  const past = [...state.past];
  const [last] = past.splice(past.length - 1, 1);
  const now = last ?? null;
  const future = state.now ? [state.now, ...state.future] : state.future;

  return transfer({ past, now, future });
}

// 🛠️

// TODO: Most likely there's a more performant solution
function fill(state: State): State {
  if (state.future.length >= QUEUE_SIZE) return state;

  let reducedPool = internal.pool.reduce(
    ({ past, pool }: { past: Set<string>; pool: Track[] }, track: Track) => {
      if (past.has(track.id))
        return {
          past: past.difference(new Set(track.id)),
          pool,
        };

      return {
        past,
        pool: [...pool, track],
      };
    },
    { past: new Set(state.past.map((t) => t.id)), pool: [] },
  ).pool;

  if (reducedPool.length === 0) {
    reducedPool = internal.pool;
  }

  const poolSelection = arrayShuffle(reducedPool).slice(0, QUEUE_SIZE - state.future.length);
  return add(state, poolSelection);
}
