import { derived, get } from 'svelte/store';
import { runCommand, setAccountData } from './api';
import {
  muteSpacePlan,
  reconcilePlan,
  spaceMutedRoomIds,
  unmuteSpacePlan,
  withSpaceMutes,
  type Plan,
  type RoomMode
} from './mute-model';
import { MUTED_SPACES_EVENT, mutedSpaces, mutedSpacesLoaded } from './muted-spaces';
import { rooms } from './store';

export const spaceMutedRooms = derived([rooms, mutedSpaces], ([$rooms, $muted]) => spaceMutedRoomIds($rooms, $muted.spaces));

export const effectiveRooms = derived([rooms, spaceMutedRooms], ([$rooms, $muted]) => withSpaceMutes($rooms, $muted));

let queue: Promise<void> = Promise.resolve();

export function setRoomMode(roomId: string, mode: RoomMode | null): Promise<void> {
  return runCommand({ op: 'set_room_notification_mode', room_id: roomId, mode }).then(() => undefined);
}

async function apply(plan: Plan): Promise<void> {
  await setAccountData(MUTED_SPACES_EVENT, { ...plan.state });
  mutedSpaces.set(plan.state);
  const results = await Promise.allSettled(plan.changes.map((c) => setRoomMode(c.roomId, c.mode)));
  const failed = results.find((r): r is PromiseRejectedResult => r.status === 'rejected');
  if (failed) throw failed.reason;
}

function enqueue(job: () => Promise<void>): Promise<void> {
  const run = queue.then(job);
  queue = run.catch(() => undefined);
  return run;
}

export function setSpaceMuted(spaceId: string, muted: boolean): Promise<void> {
  return enqueue(async () => {
    const state = get(mutedSpaces);
    const all = get(rooms);
    await apply(muted ? muteSpacePlan(state, spaceId, all) : unmuteSpacePlan(state, spaceId, all));
  });
}

export function startMuteSync(): () => void {
  const reconcile = () => {
    if (!mutedSpacesLoaded() || get(mutedSpaces).spaces.length === 0) return;
    void enqueue(async () => {
      const plan = reconcilePlan(get(mutedSpaces), get(rooms));
      if (plan.changes.length > 0) await apply(plan);
    }).catch(() => undefined);
  };
  const stopRooms = rooms.subscribe(reconcile);
  const stopMuted = mutedSpaces.subscribe(reconcile);
  return () => {
    stopRooms();
    stopMuted();
  };
}
