import type { RefObject } from 'react';

import isElectron from 'is-electron';
import { useEffect, useImperativeHandle, useRef, useState } from 'react';

import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types';
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
import { useSettingsStore } from '/@/renderer/store';
import { PlayerStatus } from '/@/shared/types/types';

export interface MpvPlayerEngineHandle extends AudioPlayer {}

interface MpvPlayerEngineProps {
    currentSrc: string | undefined;
    isMuted: boolean;
    isTransitioning: boolean;
    nextSrc: string | undefined;
    onEnded: () => void;
    onProgress: (e: PlayerOnProgressProps) => void;
    playerRef: RefObject<MpvPlayerEngineHandle | null>;
    playerStatus: PlayerStatus;
    speed?: number;
    volume: number;
}

const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
const mpvPlayerListener = isElectron() ? window.api.mpvPlayerListener : null;
const ipc = isElectron() ? window.api.ipc : null;

const PROGRESS_UPDATE_INTERVAL = 250;
const TRANSITION_PROGRESS_INTERVAL = 10;

export const MpvPlayerEngine = (props: MpvPlayerEngineProps) => {
    const {
        currentSrc,
        isMuted,
        isTransitioning,
        nextSrc,
        onEnded,
        onProgress,
        playerRef,
        playerStatus,
        speed,
        volume,
    } = props;

    const [internalVolume, setInternalVolume] = useState(volume / 100 || 0);
    const [duration] = useState(0);
    const [previousCurrentSrc, setPreviousCurrentSrc] = useState<string | undefined>(currentSrc);

    const progressIntervalRef = useRef<NodeJS.Timeout | null>(null);
    const isInitializedRef = useRef<boolean>(false);
    const hasPopulatedQueueRef = useRef<boolean>(false);
    const isMountedRef = useRef<boolean>(true);
    const currentSrcRef = useRef<string | undefined>(currentSrc);
    const nextSrcRef = useRef<string | undefined>(nextSrc);

    const mpvExtraParameters = useSettingsStore((store) => store.playback.mpvExtraParameters);
    const mpvProperties = useSettingsStore((store) => store.playback.mpvProperties);

    // Start the mpv instance on startup
    useEffect(() => {
        isMountedRef.current = true;

        const initializeMpv = async () => {
            const isRunning: boolean | undefined = await mpvPlayer?.isRunning();

            if (!isRunning) {
                const properties: Record<string, any> = {
                    // speed: usePlayerStore.getState().speed,
                    ...getMpvProperties(mpvProperties),
                };

                await mpvPlayer?.initialize({
                    extraParameters: mpvExtraParameters,
                    properties,
                });

                mpvPlayer?.volume(properties.volume);
                isInitializedRef.current = true;
            } else {
                isInitializedRef.current = true;
            }

            // After initialization, populate the queue if currentSrc is available
            const latestCurrentSrc = currentSrcRef.current;
            const latestNextSrc = nextSrcRef.current;
            if (latestCurrentSrc && !hasPopulatedQueueRef.current && mpvPlayer) {
                mpvPlayer.setQueue(latestCurrentSrc, latestNextSrc, true);
                hasPopulatedQueueRef.current = true;
                setPreviousCurrentSrc(latestCurrentSrc);
            }
        };

        initializeMpv();

        return () => {
            isMountedRef.current = false;
            mpvPlayer?.quit();
            isInitializedRef.current = false;
            hasPopulatedQueueRef.current = false;
        };
    }, [mpvExtraParameters, mpvProperties]);

    useEffect(() => {
        currentSrcRef.current = currentSrc;
        nextSrcRef.current = nextSrc;
    }, [currentSrc, nextSrc]);

    // Update volume
    useEffect(() => {
        if (!mpvPlayer) {
            return;
        }

        const vol = volume / 100 || 0;
        queueMicrotask(() => {
            setInternalVolume(vol);
        });
        mpvPlayer.volume(volume);
    }, [volume]);

    // Update mute status
    useEffect(() => {
        if (!mpvPlayer) {
            return;
        }

        mpvPlayer.mute(isMuted);
    }, [isMuted]);

    // Update speed/playback rate
    useEffect(() => {
        if (!mpvPlayer) {
            return;
        }

        if (!speed) {
            return;
        }

        mpvPlayer.setProperties({ speed });
    }, [speed]);

    // Handle current song changes - update queue position 0
    // When currentSrc changes, we need to update the queue
    useEffect(() => {
        if (!mpvPlayer) {
            return;
        }

        // If currentSrc changed, update the queue
        if (currentSrc !== previousCurrentSrc) {
            if (currentSrc) {
                // Set current song at position 0 and next song at position 1
                mpvPlayer.setQueue(currentSrc, nextSrc, playerStatus !== PlayerStatus.PLAYING);
                setPreviousCurrentSrc(currentSrc);
            } else {
                // Clear queue if no current song
                mpvPlayer.setQueue(undefined, undefined, true);
                setPreviousCurrentSrc(undefined);
            }
        } else {
            // If currentSrc hasn't changed but nextSrc has, update position 1
            // This happens when the next song changes but current song stays the same
            if (currentSrc) {
                mpvPlayer.setQueueNext(nextSrc);
            }
        }
    }, [currentSrc, previousCurrentSrc, nextSrc, playerStatus]);

    // Handle play/pause status
    useEffect(() => {
        if (!mpvPlayer) {
            return;
        }

        if (playerStatus === PlayerStatus.PLAYING) {
            mpvPlayer.play();
        } else if (playerStatus === PlayerStatus.PAUSED) {
            mpvPlayer.pause();
        }
    }, [playerStatus]);

    // Set up progress tracking
    useEffect(() => {
        if (progressIntervalRef.current) {
            clearInterval(progressIntervalRef.current);
        }

        const updateProgress = async () => {
            if (!mpvPlayer || !isMountedRef.current) {
                return;
            }

            try {
                const time = await mpvPlayer.getCurrentTime();
                if (time !== undefined && isMountedRef.current) {
                    onProgress({
                        played: time / (duration || time + 10),
                        playedSeconds: time,
                    });
                }
            } catch {
                // Handle error silently
            }
        };

        if (currentSrc) {
            const interval = isTransitioning
                ? TRANSITION_PROGRESS_INTERVAL
                : PROGRESS_UPDATE_INTERVAL;
            progressIntervalRef.current = setInterval(updateProgress, interval);
            updateProgress();
        }

        return () => {
            isMountedRef.current = false;
            if (progressIntervalRef.current) {
                clearInterval(progressIntervalRef.current);
                progressIntervalRef.current = null;
            }
        };
    }, [currentSrc, isTransitioning, duration, onProgress]);

    useEffect(() => {
        if (!mpvPlayerListener) {
            return;
        }

        const handleOnEnded = () => {
            onEnded();
        };

        mpvPlayerListener.rendererAutoNext(handleOnEnded);

        return () => {
            ipc?.removeAllListeners('renderer-player-auto-next');
        };
    }, [nextSrc, onEnded]);

    useImperativeHandle<MpvPlayerEngineHandle, MpvPlayerEngineHandle>(playerRef, () => ({
        decreaseVolume(by: number) {
            const newVol = Math.max(0, internalVolume - by / 100);
            setInternalVolume(newVol);
            if (mpvPlayer) {
                mpvPlayer.volume(newVol * 100);
            }
        },
        increaseVolume(by: number) {
            const newVol = Math.min(1, internalVolume + by / 100);
            setInternalVolume(newVol);
            if (mpvPlayer) {
                mpvPlayer.volume(newVol * 100);
            }
        },
        pause() {
            if (mpvPlayer) {
                mpvPlayer.pause();
            }
        },
        play() {
            if (mpvPlayer) {
                mpvPlayer.play();
            }
        },
        seekTo(seekTo: number) {
            if (mpvPlayer) {
                mpvPlayer.seekTo(seekTo);
            }
        },
        setVolume(vol: number) {
            const volDecimal = vol / 100 || 0;
            setInternalVolume(volDecimal);
            if (mpvPlayer) {
                mpvPlayer.volume(vol);
            }
        },
    }));

    return <div id="mpv-player-engine" style={{ display: 'none' }} />;
};

MpvPlayerEngine.displayName = 'MpvPlayerEngine';
