import React, { useState, useEffect, useCallback, useRef } from 'react';
import getFileDownloadUrl from '@/api/server/files/getFileDownloadUrl';
import getFileStreamUrl from '../api/server/files/getFileStreamUrl';
import { getFileContents } from '../api/server/files';
import Spinner from '@/components/elements/Spinner';
import { themeColors } from '../colorExtractor';
import { ViewerWrapper, ViewerContainer } from './styles';
import { MediaViewerProps, ResumeState } from './types';
import { isFormatSupported, getMimeType } from './utils';
import ImageViewer from './ImageViewer';
import VideoPlayer from './VideoPlayer';
import AudioPlayer from './AudioPlayer';

const MAX_FILE_SIZE = 250 * 1024 * 1024;
const STREAM_REFRESH_MS = 5 * 60 * 1000;

const MediaViewer: React.FC<MediaViewerProps> = ({ uuid, filePath, fileType, readOnly = false }) => {
    const [fileUrl, setFileUrl] = useState<string | null>(null);
    const [loading, setLoading] = useState(true);
    const [buffering, setBuffering] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [blobUrl, setBlobUrl] = useState<string | null>(null);
    const [mediaBlob, setMediaBlob] = useState<Blob | null>(null);
    const [blobPartial, setBlobPartial] = useState(false);
    const [downloadProgress, setDownloadProgress] = useState(0);
    const [isDownloading, setIsDownloading] = useState(false);
    const xhrRef = useRef<XMLHttpRequest | null>(null);
    const blobUrlRef = useRef<string | null>(null);
    const downloadUrlRef = useRef<string | null>(null);
    const [streamMode, setStreamMode] = useState(false);
    const [streamFallbackTried, setStreamFallbackTried] = useState(false);
    const streamTimeRef = useRef(0);
    const streamPlayingRef = useRef(false);
    const [resumeState, setResumeState] = useState<ResumeState | null>(null);
    const [isRequestingFullDownload, setIsRequestingFullDownload] = useState(false);

    useEffect(() => {
        return () => {
            if (xhrRef.current) {
                xhrRef.current.abort();
            }
            if (blobUrlRef.current) {
                URL.revokeObjectURL(blobUrlRef.current);
                blobUrlRef.current = null;
            }
            downloadUrlRef.current = null;
        };
    }, [uuid, filePath]);

    const startBlobDownload = useCallback((url: string) => {
        if (xhrRef.current) {
            xhrRef.current.abort();
        }
        setStreamMode(false);
        setBlobPartial(false);
        setIsDownloading(true);
        setLoading(true);
        setError(null);
        setDownloadProgress(0);
        setMediaBlob(null);
        setBlobUrl(null);
        setFileUrl(null);

        const xhr = new XMLHttpRequest();
        xhrRef.current = xhr;
        xhr.open('GET', url);
        xhr.responseType = 'blob';

        xhr.onprogress = (e) => {
            if (e.lengthComputable) {
                if (e.total > MAX_FILE_SIZE) {
                    xhr.abort();
                    setError(`File is too large to preview (${Math.round(e.total / 1024 / 1024)}MB). Max size is 250MB.`);
                    setIsDownloading(false);
                    setLoading(false);
                    return;
                }
                setDownloadProgress((e.loaded / e.total) * 100);
            }
        };

        xhr.onload = () => {
            if (xhr.status === 200) {
                const blob = xhr.response;
                if (blob.size < 10240) {
                    const reader = new FileReader();
                    reader.onload = () => {
                        const text = reader.result as string;
                        if (text.includes('{"') || text.includes('<!DOCTYPE') || text.includes('<html')) {
                            setError('Failed to download media: ' + (text.substring(0, 100) + '...'));
                        } else {
                            processBlob(blob);
                        }
                    };
                    reader.readAsText(blob);
                    return;
                }
                processBlob(blob);
            } else {
                setError('Failed to download media file (Status: ' + xhr.status + ')');
                setIsDownloading(false);
                setLoading(false);
            }
        };

        const processBlob = (blob: Blob) => {
            const mimeType = getMimeType(filePath, blob.type);
            const newBlob = new Blob([blob], { type: mimeType });
            const objectUrl = URL.createObjectURL(newBlob);
            blobUrlRef.current = objectUrl;

            setMediaBlob(newBlob);
            setBlobPartial(false);
            setBlobUrl(objectUrl);
            setFileUrl(objectUrl);
            setIsDownloading(false);
            setLoading(false);
        };

        xhr.onerror = () => {
            setError('Network error while downloading media.');
            setIsDownloading(false);
            setLoading(false);
        };

        xhr.send();
    }, [filePath]);

    const requestFullDownload = useCallback(() => {
        if (isDownloading || isRequestingFullDownload) return;
        setIsRequestingFullDownload(true);
        const finalize = () => setIsRequestingFullDownload(false);
        if (downloadUrlRef.current) {
            startBlobDownload(downloadUrlRef.current);
            finalize();
            return;
        }
        getFileDownloadUrl(uuid, filePath)
            .then((url) => {
                downloadUrlRef.current = url;
                startBlobDownload(url);
            })
            .finally(finalize);
    }, [filePath, isDownloading, isRequestingFullDownload, startBlobDownload, uuid]);

    const refreshStreamUrl = useCallback(() => {
        setResumeState({
            time: streamTimeRef.current,
            wasPlaying: streamPlayingRef.current,
            token: Date.now(),
        });
        return getFileStreamUrl(uuid, filePath)
            .then((url) => {
                setStreamMode(true);
                setStreamFallbackTried(false);
                setFileUrl(url);
            });
    }, [uuid, filePath]);

    useEffect(() => {
        if (xhrRef.current) {
            xhrRef.current.abort();
        }
        setLoading(true);
        setError(null);
        setBlobUrl(null);
        setMediaBlob(null);
        setBlobPartial(false);
        setDownloadProgress(0);
        setIsDownloading(false);
        setStreamMode(false);
        setStreamFallbackTried(false);
        if (blobUrlRef.current) {
            URL.revokeObjectURL(blobUrlRef.current);
            blobUrlRef.current = null;
        }

        if (fileType === 'image' && filePath.toLowerCase().endsWith('.svg')) {
            getFileContents(uuid, filePath)
                .then((content) => {
                    const objectUrl = URL.createObjectURL(new Blob([content], { type: 'image/svg+xml' }));
                    blobUrlRef.current = objectUrl;
                    setBlobUrl(objectUrl);
                    setFileUrl(objectUrl);
                    setLoading(false);
                })
                .catch(err => {
                    setError(err.message || 'Failed to load SVG preview');
                    setLoading(false);
                });
            return;
        }

        if (fileType === 'image') {
            getFileDownloadUrl(uuid, filePath)
                .then(url => {
                    downloadUrlRef.current = url;
                    setFileUrl(url);
                    setLoading(false);
                })
                .catch(err => {
                    setError(err.message || 'Failed to load media file');
                    setLoading(false);
                });
            return;
        }

        const tryStream = fileType === 'video' || fileType === 'audio';
        if (tryStream) {
            getFileStreamUrl(uuid, filePath)
                .then(url => {
                    setStreamMode(true);
                    setFileUrl(url);
                    setLoading(false);
                })
                .catch(() => {
                    getFileDownloadUrl(uuid, filePath)
                        .then(url => {
                            downloadUrlRef.current = url;
                            startBlobDownload(url);
                        })
                        .catch(err => {
                            setError(err.message || 'Failed to load media file');
                            setLoading(false);
                        });
                });
            return;
        }

        getFileDownloadUrl(uuid, filePath)
            .then(url => {
                downloadUrlRef.current = url;
                startBlobDownload(url);
            })
            .catch(err => {
                setError(err.message || 'Failed to load media file');
                setLoading(false);
            });
    }, [uuid, filePath, fileType, startBlobDownload]);

    useEffect(() => {
        if (!streamMode) return;
        const interval = setInterval(() => {
            refreshStreamUrl().catch(() => {});
        }, STREAM_REFRESH_MS);
        return () => clearInterval(interval);
    }, [streamMode, refreshStreamUrl]);

    const handleMediaError = (e: React.SyntheticEvent<HTMLVideoElement | HTMLAudioElement>) => {
        const mediaElement = e.currentTarget;
        const error = mediaElement.error;

        if (!error) return;

        if (streamMode && !streamFallbackTried) {
            setStreamFallbackTried(true);
            refreshStreamUrl()
                .catch(() => {
                    if (downloadUrlRef.current) {
                        startBlobDownload(downloadUrlRef.current);
                        return;
                    }
                    getFileDownloadUrl(uuid, filePath)
                        .then(url => {
                            downloadUrlRef.current = url;
                            startBlobDownload(url);
                        })
                        .catch(err => {
                            setError(err.message || 'Failed to load media file');
                            setLoading(false);
                        });
                });
            return;
        }

        const errorMessages: Record<number, string> = {
            1: 'Media loading aborted by user',
            2: 'Network error while loading media - check your connection',
            3: 'Media decoding failed - file may be corrupted',
            4: 'Media format not supported by your browser',
        };

        if (error.code !== 1) {
            setError(errorMessages[error.code] || `Media error (code: ${error.code})`);
        }
    };

    if (loading || isDownloading) {
        return (
            <ViewerWrapper>
                <ViewerContainer>
                    <div className="flex-1 flex flex-col items-center justify-center p-6">
                        <Spinner size="large" />
                        {isDownloading && (
                            <div className="mt-4 w-64">
                                <div className="flex justify-between text-xs text-neutral-300 mb-1">
                                    <span>Downloading for preview...</span>
                                    <span>{Math.round(downloadProgress)}%</span>
                                </div>
                                <div
                                    className="w-full bg-gray-700 h-2 overflow-hidden"
                                    style={{ borderRadius: themeColors.borderRadius.component }}
                                >
                                    <div
                                        className="h-full bg-primary-500 transition-all duration-200"
                                        style={{ width: `${downloadProgress}%` }}
                                    />
                                </div>
                                <p className="text-xs text-neutral-300 mt-2 text-center">
                                    Large files are downloaded to memory for smooth playback.
                                </p>
                            </div>
                        )}
                    </div>
                </ViewerContainer>
            </ViewerWrapper>
        );
    }

    if (error || !fileUrl) {
        return (
            <ViewerWrapper>
                <ViewerContainer>
                    <div className="flex-1 flex flex-col items-center justify-center text-red-400 text-center max-w-md p-6">
                        <svg className="w-16 h-16 mx-auto mb-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                        </svg>
                        <p className="text-lg font-semibold mb-2">Playback Failed</p>
                        <p className="text-sm text-neutral-300 mb-4">
                            {error?.includes('DEMUXER_ERROR')
                                ? 'The video format or codec is not supported by your browser.'
                                : (error || 'Unknown error')}
                        </p>

                        {blobUrl && (
                            <a
                                href={blobUrl}
                                download={filePath.split('/').pop()}
                                className="inline-flex items-center px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white transition-colors duration-200 text-sm font-medium"
                                style={{ borderRadius: themeColors.borderRadius.component }}
                            >
                                <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
                                </svg>
                                Download to View Locally
                            </a>
                        )}

                        {!isFormatSupported(filePath) && (
                            <p className="text-xs text-yellow-500 mt-4">
                                Note: The file extension ".{filePath.split('.').pop()}" might not be supported by your browser.
                            </p>
                        )}
                        <p className="text-xs text-neutral-300 mt-4">File: {filePath}</p>
                    </div>
                </ViewerContainer>
            </ViewerWrapper>
        );
    }

    return (
        <ViewerWrapper>
            <ViewerContainer>
                {fileType === 'image' && (
                    <ImageViewer src={fileUrl} alt={filePath} />
                )}
                {fileType === 'video' && (
                    <>
                        <VideoPlayer
                            src={fileUrl}
                            blobUrl={blobUrl}
                            fileName={filePath.split('/').pop() || ''}
                            isStreaming={streamMode}
                            resumeState={resumeState}
                            onError={handleMediaError}
                            onWaiting={() => setBuffering(true)}
                            onPlaying={() => setBuffering(false)}
                            onTimeUpdate={(time: number) => {
                                streamTimeRef.current = time;
                            }}
                            onPlayingChange={(playing: boolean) => {
                                streamPlayingRef.current = playing;
                            }}
                        />
                        {buffering && (
                            <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
                                <Spinner size="large" />
                            </div>
                        )}
                    </>
                )}
                {fileType === 'audio' && (
                    <AudioPlayer
                        src={fileUrl}
                        blobUrl={blobUrl}
                        blob={mediaBlob}
                        blobPartial={blobPartial}
                        isStreaming={streamMode}
                        resumeState={resumeState}
                        filePath={filePath}
                        uuid={uuid}
                        fileName={filePath.split('/').pop() || ''}
                        onRequestFullDownload={requestFullDownload}
                        onError={handleMediaError}
                        onTimeUpdate={(time: number) => {
                            streamTimeRef.current = time;
                        }}
                        onPlayingChange={(playing: boolean) => {
                            streamPlayingRef.current = playing;
                        }}
                        readOnly={readOnly}
                    />
                )}
            </ViewerContainer>
        </ViewerWrapper>
    );
};

export default MediaViewer;
