const CALL_SOUND_URL = '/sounds/header-sound.mp3';

let sharedAudio = null;
let unlocked = false;

function getCallAudio() {
    if (!sharedAudio) {
        sharedAudio = new Audio(CALL_SOUND_URL);
        sharedAudio.preload = 'auto';
    }
    return sharedAudio;
}

/**
 * Call once after a user gesture (click/tap) so later plays are allowed.
 */
export function unlockQueueCallSound() {
    if (unlocked) return;
    try {
        const audio = getCallAudio();
        audio.muted = true;
        const playPromise = audio.play();
        if (playPromise?.then) {
            playPromise
                .then(() => {
                    audio.pause();
                    audio.currentTime = 0;
                    audio.muted = false;
                    unlocked = true;
                })
                .catch(() => {
                    // Still mark unlocked so we keep trying on real calls.
                    audio.muted = false;
                    unlocked = true;
                });
        } else {
            audio.muted = false;
            unlocked = true;
        }
    } catch {
        unlocked = true;
    }
}

/**
 * Play the queue-call notification chime.
 * Safe to call repeatedly; restarts from the beginning each time.
 * @param {{ onEnded?: () => void }} [options]
 */
export function playQueueCallSound({ onEnded } = {}) {
    let endedNotified = false;
    const notifyEnded = () => {
        if (endedNotified || !onEnded) return;
        endedNotified = true;
        onEnded();
    };

    try {
        const audio = getCallAudio();
        audio.muted = false;
        audio.currentTime = 0;

        if (onEnded) {
            audio.addEventListener('ended', notifyEnded, { once: true });
        }

        const playPromise = audio.play();
        if (playPromise?.catch) {
            playPromise.catch((error) => {
                console.warn('Could not play queue call sound:', error);
                notifyEnded();
            });
        }
    } catch (error) {
        console.warn('Could not play queue call sound:', error);
        notifyEnded();
    }
}

/**
 * Build a stable map of stationId -> ticket number for currently serving tickets.
 */
export function servingSnapshot(tickets) {
    const map = {};
    for (const ticket of tickets) {
        if (ticket?.station?.id != null && ticket?.number != null) {
            map[String(ticket.station.id)] = String(ticket.number);
        }
    }
    return map;
}

/**
 * Returns true if any station got a new/different ticket number vs the previous snapshot.
 */
export function hasNewServingCall(previous, next) {
    return getNewServingCalls(previous, next).length > 0;
}

/**
 * Returns stations whose assigned ticket number changed since the previous snapshot.
 * Pass tickets when station names are needed for voice announcements.
 */
export function getNewServingCalls(previous, nextOrTickets) {
    if (!previous) return [];

    const tickets = Array.isArray(nextOrTickets)
        ? nextOrTickets
        : Object.entries(nextOrTickets).map(([stationId, number]) => ({
              station: { id: stationId },
              number,
          }));

    const calls = [];
    for (const ticket of tickets) {
        if (ticket?.station?.id == null || ticket?.number == null) continue;

        const stationId = String(ticket.station.id);
        const number = String(ticket.number);
        if (previous[stationId] === number) continue;

        calls.push({
            stationId,
            ticketNumber: number,
            stationName: ticket.station.name ?? `Stesen ${stationId}`,
        });
    }

    return calls;
}
