import { ref, computed, watch, onUnmounted } from 'vue'
import { nextTick } from 'vue'
import { store } from '../store'

export function use_timeline(fetch_fn: (before?: number) => Promise<void>) {
    let live_interval: ReturnType<typeof setInterval> | null = null
    let countdown_interval: ReturnType<typeof setInterval> | null = null
    let debounce_timer: ReturnType<typeof setTimeout> | null = null

    const MAX_FAILURES = 3
    const failure_count = ref(0)
    const timeline_position_debounced = ref(store.timeline_position)

    const is_live_debounced = computed(() =>
        timeline_position_debounced.value >= store.timeline_max - 2000
    )

    const manual_refresh = async () => {
        store.unfreeze()
        await fetch_fn()
        await nextTick()
        store.timeline_position = store.timeline_max
        if (live_interval) clearInterval(live_interval)
        if (countdown_interval) clearInterval(countdown_interval)
        store.live_countdown = store.refresh_interval_seconds
        live_interval = setInterval(async () => {
            await safe_fetch()
            store.live_countdown = store.refresh_interval_seconds
        }, store.refresh_interval_seconds * 1000)
        countdown_interval = setInterval(() => {
            if (store.live_countdown > 0) store.live_countdown--
        }, 1000)
    }

    const freeze = () => store.freeze()

    const snap_to_live = async () => {
        store.unfreeze()
        await fetch_fn()
        await nextTick()
        store.timeline_position = store.timeline_max
    }

    // NOTE: This is in place to avoid the intervals breaking on server issues and constantly polling the backend.
    const safe_fetch = async () => {
        try {
            await fetch_fn()
            failure_count.value = 0
        } catch (e) {
            failure_count.value++
            console.error(`Fetch failed (${failure_count.value}/${MAX_FAILURES})`, e)
            if (failure_count.value >= MAX_FAILURES) {
                store.freeze()
                store.server_unreachable = true
                if (live_interval) { clearInterval(live_interval); live_interval = null }
                if (countdown_interval) { clearInterval(countdown_interval); countdown_interval = null }
            }
        }
    }

    watch(() => store.is_live, (live) => {
        if (live && !store.is_frozen) {
            store.live_countdown = store.refresh_interval_seconds
            live_interval = setInterval(async () => {
                await safe_fetch()
                store.live_countdown = store.refresh_interval_seconds
            }, store.refresh_interval_seconds * 1000)
            countdown_interval = setInterval(() => {
                if (store.live_countdown > 0) store.live_countdown--
            }, 1000)
        } else {
            if (live_interval) clearInterval(live_interval)
            if (countdown_interval) clearInterval(countdown_interval)
            store.live_countdown = store.refresh_interval_seconds
        }
    }, { immediate: true })

    watch(() => store.refresh_interval_seconds, () => {
        if (store.is_live) {
            if (live_interval) clearInterval(live_interval)
            if (countdown_interval) clearInterval(countdown_interval)
            store.live_countdown = store.refresh_interval_seconds
            live_interval = setInterval(async () => {
                await safe_fetch()
                store.live_countdown = store.refresh_interval_seconds
            }, store.refresh_interval_seconds * 1000)
            countdown_interval = setInterval(() => {
                if (store.live_countdown > 0) store.live_countdown--
            }, 1000)
        }
    })

    watch(() => store.timeline_position, (val) => {
        if (debounce_timer) clearTimeout(debounce_timer)
        debounce_timer = setTimeout(() => {
            timeline_position_debounced.value = val
        }, 150)
    })

    onUnmounted(() => {
        if (live_interval) clearInterval(live_interval)
        if (countdown_interval) clearInterval(countdown_interval)
        if (debounce_timer) clearTimeout(debounce_timer)
    })

    return { timeline_position_debounced, is_live_debounced, manual_refresh, freeze, snap_to_live }
}
