// Entry point. Wires the shared Socket.IO connection to each module's
// render functions and kicks off the one-time init for every tab.
// Socket.IO itself is still loaded via a classic <script> tag in
// index.html (it writes to window.io), so this module just imports the
// shared instance from socket.js.

import {socket} from './socket.js';
import {updateLogo} from './logo.js';
import {updateBandMeters, updateSignalMeter} from './meters.js';
import {renderNotifications, updateGraceBanner} from './notifications.js';
import {pushToast, dismissToast} from '../features/toasts.js';
import {
    updateDirector,
    updateBeatPulse,
    updateSuperbeat,
    updateThinking,
    updateCycleBar,
    updatePanelPulse,
} from '../features/director_panel.js';
import {
    updateLumiSliders,
    updateStats,
    updateSystems,
    initQuickActions,
    initSliders,
    reflectEngineState,
} from './show.js';
import {renderLog, initLog} from './log.js';
import {initAudio} from '../features/audio_input.js';
import {initAutoplay} from '../features/autoplay.js';
import {initTelemetry} from '../features/telemetry_modal.js';
import {initMacAudio} from '../features/mac_audio_modal.js';
import {initTabs} from '../features/tabs.js';
import {initFixtures} from '../features/fixtures.list.js';
import {initDmx} from '../features/dmx.js';
import {initDmxDiscover} from '../features/dmx_discover.js';
import {initGovee} from '../features/govee.js';
import {initDirectorConfig, updateDirectorPauseStatus} from '../features/director.js';
import {initMidiConfig} from '../features/midi.js';
import {initStarterCta} from '../features/starter_cta.js';
import {initUpgradeCta, noteUpgradeCtaSignal} from '../features/upgrade_cta.js';
import {initGoveeCta} from '../features/govee_cta.js';
import {initLockedTabs} from '../features/locked_tabs.js';
import {initFeedback} from '../features/feedback.js';
import {initCatalogRefresh} from '../features/catalog_refresh.js';
import {initLicense} from '../features/license.js';
import {initUpdates} from './updates.js';
import {initWelcome} from '../features/welcome.js';
import {initBYOKSetup} from '../features/byok_setup.js';
import {initAudioWizard} from '../features/audio_wizard.js';
import {initPresenceTracker} from './presence_tracker.js';
import {initPersonalityPicker, syncPersonalityFromState} from '../features/personality_picker.js';
import {initCrashTelemetry, initUpgradeLinks} from './utils.js';

// Socket connection health surfaces as an error toast instead of a
// status bubble. Socket.IO auto-reconnects, so we just dismiss on
// reconnect — no manual retry UI needed.
socket.on('connect', () => {
    dismissToast('connection-lost');
});
socket.on('disconnect', () => {
    pushToast({
        key: 'connection-lost',
        level: 'error',
        title: 'Lost connection to Conductør',
        detail: 'Trying to reconnect…',
        dismissible: false,
    });
});

// KL-82: license activations (manual paste OR conductor:// deeplink)
// emit `license:activated` server-side. A single global listener here
// gives both flows the same celebratory toast + reload, so a paying
// user never lingers behind a stale paywall after activation.
const TIER_LABELS_FOR_TOAST = {pro: 'Pro', venue: 'Venue', starter: 'Starter'};
socket.on('license:activated', (payload) => {
    const newTier = (payload && payload.to_tier) || 'pro';
    const label = TIER_LABELS_FOR_TOAST[newTier] || newTier;
    pushToast({
        key: 'license-activated',
        level: 'info',
        title: `Welcome to ${label}.`,
        detail: 'Unlocking your tabs…',
        dismissible: false,
    });
    // Reload so the server re-renders with the hot-swapped tier
    // (locked tabs unlock, sidebar badge updates, paywall blurs
    // clear). Server-side tier swap already happened before this
    // event fired, so the next render is authoritative.
    setTimeout(() => {
        dismissToast('license-activated');
        window.location.reload();
    }, 1500);
});

// 10Hz state push — every live surface on the dashboard reads from this.
socket.on('state_update', (data) => {
    updateBandMeters(data.smoothed);
    updateSignalMeter(data.smoothed);
    // Cheap "have we heard signal?" hook for the contextual upgrade
    // toast (KL-16). Short-circuits internally once the flag is set
    // so the 10Hz state push doesn't take a perf hit per tick.
    noteUpgradeCtaSignal(data.smoothed);
    updateDirector(data.director);
    if (data.director && data.director.params) {
        updateLumiSliders(data.director.params);
    }
    // KL-64 personality cues — each one short-circuits internally
    // when its trigger isn't met, so it's safe to call all of them
    // on every 10Hz tick.
    updateBeatPulse(data.beat_energy);
    updateSuperbeat(data.superbeat_active);
    if (data.director) {
        updateThinking(data.director.pending);
        updateCycleBar(data.director.last_decision_ts);
        updatePanelPulse(
            data.director.emotion,
            data.director.params && data.director.params.mood_color,
        );
        // KL-101: paused pill + marathon countdown.
        updateDirectorPauseStatus(data.director);
        // KL-69: keep the personality chips in sync if active changes
        // from a non-UI source.
        syncPersonalityFromState(data.director.active_personality);
    }
    updateStats(data);
    // KL-67: hardware-status badges (DMX + Govee) in the director HUD.
    // The render is internally cached so this is cheap to call at 10Hz.
    updateSystems(data.dmx_status, data.govee_status);
    if (data.logo) updateLogo(data.logo);
    updateGraceBanner(data.grace_period, data.grace_message);
    if (data.recent_log) renderLog(data.recent_log);
    renderNotifications(data.notifications || []);
    if (typeof data.engine_playing === 'boolean') {
        reflectEngineState(data.engine_playing);
    }
});

// Crash telemetry first so window-level error listeners are bound before
// any other init code runs and could throw.
initCrashTelemetry();

// KL-89: rewrite the static <a data-upgrade-surface="..."> tags so they
// carry surface + distinct_id query params into the pricing page. Runs
// before any tab init since these are visible at page load.
initUpgradeLinks();

// One-time init for every tab. Order matters for the tab switcher:
// it must run before anything else that expects a visible panel so the
// localStorage-restored selection has applied before IIFEs bind.
initTabs();
initLockedTabs();
initQuickActions();
initSliders();
initLog();
initAudio();
initMacAudio();
initTelemetry();
initAutoplay();
initFixtures();
initDmx();
initDmxDiscover({onAdded: () => {
    // Trigger a Fixtures tab refresh after RDM auto-add.
    if (typeof window.conductorRefreshFixtures === 'function') {
        window.conductorRefreshFixtures();
    }
}});
initGovee();
initDirectorConfig();
initMidiConfig();
initStarterCta();
initUpgradeCta();
initGoveeCta();
initFeedback();
initCatalogRefresh();
initLicense();
initUpdates();
// Welcome modal goes last so all other surfaces are wired up by the
// time the overlay renders. The modal is z-indexed above everything,
// so the dashboard behind it stays interactive only after Continue —
// which is the entire point of the gate.
initWelcome();
// KL-72: first-run audio source wizard. Internally defers to the Mac
// BlackHole onboarding modal when applicable so first-run modals don't
// stack on Mac users who don't have BlackHole yet.
initAudioWizard();
// KL-182: post-activation BYOK setup modal. No-ops unless the Director
// status endpoint reports active_path='needs_byok_key' (Pro/Venue tier
// without a saved Anthropic key, relay UI gated off).
initBYOKSetup();
// KL-101: presence tracker — pings backend on dashboard activity so
// the director knows whether a human is around (idle-pause feature).
initPresenceTracker();
// KL-69: Lumi personality chips in the director panel.
initPersonalityPicker();
