import type { FC } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import {
    Card,
    GlyphButton,
    LoadingSpinner,
} from '@theorchard/suite-components';
import { GlyphIcon } from '@theorchard/suite-icons';
import cx from 'classnames';
import Hls from 'hls.js';
import {
    PAUSE,
    PAUSE_REQUESTED,
    PLAY,
    PLAY_REQUESTED,
    PRIMARY,
    START_REQUESTED,
    STOP_REQUESTED,
} from 'src/constants';
import { configureHls } from 'src/pages/productModal/components/productTracksAudioPlayer/utils';
import { useTrackAudioQuery } from 'src/types';
import AudioPlayerScrubber from './audioPlayerScrubber';

const INTL_PREFIX = 'contentReview.productTracksAudioPlayer';

export const CLASS_NAME = 'AudioPlayerSection';
export const LEGACY_CLASS_NAME = 'AudioPlayerSectionLegacy';
export const AUDIO_BTN_CLASS = 'audio-button';

export interface Props {
    tuid: number;
    title?: string;
    audioStatus: number;
    section?: string;
    audioCorrectionIds?: number[];
    isActive: boolean;
    setActivePlayer: (value: string) => void;
    onChangeAudioStatus: (value: number) => void;
    onChangeCurrentTrack: () => void;
}

export const AudioPlayerSection: FC<Props> = ({
    tuid,
    title = '',
    section = PRIMARY,
    audioStatus,
    audioCorrectionIds = [],
    onChangeAudioStatus,
    onChangeCurrentTrack,
    isActive,
    setActivePlayer,
}) => {
    const hlsRef = useRef<Hls | null>(null);
    const audioRef = useRef<HTMLAudioElement | null>(null);
    const [isAudioReady, setIsAudioReady] = useState(false);
    const [isPlaying, setIsPlaying] = useState(false);
    const [isHlsError, setIsHlsError] = useState(false);
    const [audioDuration, setAudioDuration] = useState<number | null>(null);

    const {
        data,
        error,
        loading: isLoading = true,
    } = useTrackAudioQuery({
        tuid: tuid.toString(),
        isCorrection: section === PRIMARY,
    });

    const isPrimary = section === PRIMARY;
    const isAudioRevised = audioCorrectionIds.includes(
        Number(data?.track?.tuid)
    );
    const isSecondaryWithNoAudio = !isPrimary && !isAudioRevised;
    const isDataError =
        !isLoading && Boolean(error || !data?.track?.streamingAudioUrl);
    const isError = isDataError || isHlsError;

    // Resets audio player state on tuid change
    useEffect(() => {
        pauseAudio();
        setIsAudioReady(false);
        setAudioDuration(null);
        setIsHlsError(false);
        setActivePlayer(PRIMARY);
    }, [tuid, setActivePlayer]);

    // Pause audio when switching between primary and secondary players
    useEffect(() => {
        if (!isActive && isPlaying) pauseAudio();
    }, [isActive, isPlaying]);

    // Allow to control play/pause from outside
    useEffect(() => {
        if (isLoading || !isAudioReady || !isActive) return;

        if (audioStatus === PLAY_REQUESTED || audioStatus === START_REQUESTED) {
            playAudio();
        }

        if (audioStatus === PAUSE_REQUESTED) {
            pauseAudio();
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [audioStatus]);

    // Load audio via HLS and start playing when audio ready
    useEffect(() => {
        const audio = audioRef.current;
        const hls = hlsRef.current;
        const { streamingAudioUrl } = data?.track ?? {};

        if (isLoading || error || !streamingAudioUrl || isSecondaryWithNoAudio)
            return;

        hlsRef.current = configureHls(streamingAudioUrl);
        audio?.addEventListener('loadeddata', onAudioDataLoaded);
        loadAudio(streamingAudioUrl);

        return () => {
            hls?.destroy();
            audio?.removeEventListener('loadeddata', onAudioDataLoaded);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data, isLoading]);

    const onAudioDataLoaded = () => {
        const hls = hlsRef.current;
        const isPlayRequested =
            isPrimary &&
            [PLAY_REQUESTED, START_REQUESTED].includes(audioStatus);

        setAudioDuration(hls?.latency ?? null);
        setIsAudioReady(true);
        setIsHlsError(false);

        if (isPlayRequested) playAudio();
    };

    const loadAudio = (manifestUrl?: string | undefined | null) => {
        const hls = hlsRef.current;
        const audio = audioRef.current;

        if (hls && audio && manifestUrl) {
            hls.loadSource(manifestUrl);
            hls.attachMedia(audio);
            hls.on(Hls.Events.ERROR, (_, data) => {
                const { fatal, details } = data;
                if (fatal && details !== Hls.ErrorDetails.INTERNAL_EXCEPTION)
                    setIsHlsError(true);
            });
        }
    };

    const playAudio = () => {
        void audioRef.current?.play();
        setIsPlaying(true);
    };

    const pauseAudio = () => {
        audioRef.current?.pause();
        setIsPlaying(false);
    };

    const onAudioButtonClick = () => {
        if (!isAudioReady || isError) return;

        if (isPlaying) pauseAudio();
        else playAudio();

        setActivePlayer(section);

        if (isPrimary) {
            onChangeCurrentTrack();
            onChangeAudioStatus(isPlaying ? PAUSE_REQUESTED : PLAY_REQUESTED);
        }
    };

    const renderAudioPlayerSection = () => {
        const sectionTitle =
            section == PRIMARY ? title : $t(`${INTL_PREFIX}.previousAudio`);

        return (
            <>
                <Card.Title>
                    {isPrimary && audioCorrectionIds.includes(tuid) && (
                        <GlyphIcon
                            className={`${CLASS_NAME}-revision-glyph`}
                            name="revision"
                            size={16}
                        />
                    )}
                    {sectionTitle}
                    <LoadingSpinner
                        className={`${CLASS_NAME}-loader`}
                        show={!isAudioReady && !isError}
                        size={16}
                    />
                </Card.Title>
                <Card.Body>
                    <div className={`${CLASS_NAME}-body`}>
                        <div className="playback-button-section">
                            <GlyphButton
                                className={cx(
                                    AUDIO_BTN_CLASS,
                                    isError && `${AUDIO_BTN_CLASS}-error`,
                                    !isActive && `${AUDIO_BTN_CLASS}-inactive`
                                )}
                                data-testid="audio-button"
                                name={!isPlaying ? PLAY : PAUSE}
                                variant="primary"
                                onClick={onAudioButtonClick}
                            />
                        </div>
                        <AudioPlayerScrubber
                            audio={audioRef.current}
                            duration={audioDuration}
                            disabled={isError || !isAudioReady}
                            isActive={isActive}
                            isPlaying={isPlaying}
                        />
                    </div>
                </Card.Body>
            </>
        );
    };

    const audioId = `${CLASS_NAME}-audio-${section}-${tuid}`;

    return (
        <div className={CLASS_NAME} data-testid={`${CLASS_NAME}-${section}`}>
            <Card suite disabled={isError || !isAudioReady}>
                <>
                    {renderAudioPlayerSection()}
                    <div className={`${CLASS_NAME}-audio-wrapper`}>
                        <audio
                            ref={audioRef}
                            id={audioId}
                            data-testid={audioId}
                            onEnded={() => {
                                onChangeAudioStatus(STOP_REQUESTED);
                                setIsPlaying(false);
                            }}
                            autoPlay={false}
                        />
                    </div>
                </>
            </Card>
        </div>
    );
};
