import { useEffect, useRef, useState } from 'react'
import { CDN_BASE } from '../../functions/Backend'
import './styles/MediaCanvas.css'

import vectorIconThrobber from '../../vectors/throbber.svg'
import vectorIconCross from '../../vectors/cross.svg'

interface PropsForMediaCanvas {
    id: string
    background: boolean
}

export default function MediaCanvas({ id, background }: PropsForMediaCanvas) {
    const canvasRef = useRef<HTMLCanvasElement>(null)
    const videoRef = useRef<HTMLVideoElement>(null)

    const [fallback, setFallback] = useState(false)
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState('')

    useEffect(() => {
        const defer: (() => void)[] = []
        const canvas = canvasRef.current!
        const video = videoRef.current!
        if (!canvas || !video) return

        video.onerror = () => {
            console.warn('Failed to load video, using fallback...')
            teardown()
            setFallback(true)
            return
        }

        // --- Initialize Canvas ---
        const gl = canvas.getContext('webgl', {
            powerPreference: 'low-power',
            preserveDrawingBuffer: true, // for download button
            premultipliedAlpha: false,
            antialias: false,
            alpha: true,
            depth: false,
        })!
        if (!gl) {
            console.error('Context failed, using fallback...')
            setFallback(true)
            return
        }

        try {
            const VERT = `
            precision mediump float;
            attribute vec2 aPos;
            uniform mat3 uMatrix;
            varying vec2 vUV;
            void main() {
                gl_Position = vec4(uMatrix * vec3(aPos, 1.0), 1.0);
                vUV = aPos;
            }`

            const FRAG = `
            precision mediump float;
            uniform sampler2D uFrame;
            varying vec2 vUV;
            void main() {
                vec2 colorUV = vec2(vUV.x, vUV.y * 0.5);
                vec2 alphaUV = vec2(vUV.x, 0.5 + vUV.y * 0.5);
                vec4 color = texture2D(uFrame, colorUV);
                float alpha = texture2D(uFrame, alphaUV).r;
                gl_FragColor = vec4(color.rgb, alpha);
            }`

            function compileShader(type: number, src: string) {
                const s = gl.createShader(type)!
                gl.shaderSource(s, src)
                gl.compileShader(s)
                return s
            }

            const prog = gl.createProgram()
            gl.attachShader(prog, compileShader(gl.VERTEX_SHADER, VERT))
            gl.attachShader(prog, compileShader(gl.FRAGMENT_SHADER, FRAG))
            gl.linkProgram(prog)
            gl.useProgram(prog)
            defer.push(() => gl.deleteProgram(prog))

            // --- Quad ---
            const buf = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buf)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1]), gl.STATIC_DRAW)
            defer.push(() => gl.deleteBuffer(buf))

            const aPos = gl.getAttribLocation(prog, 'aPos')
            gl.enableVertexAttribArray(aPos)
            gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0)
            gl.uniformMatrix3fv(gl.getUniformLocation(prog, 'uMatrix'), false, [2, 0, 0, 0, -2, 0, -1, 1, 1])

            // --- Texture ---
            const tex = gl.createTexture()
            gl.bindTexture(gl.TEXTURE_2D, tex)
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
            gl.uniform1i(gl.getUniformLocation(prog, 'uFrame'), 0)
            defer.push(() => gl.deleteTexture(tex))
            defer.push(() => gl.getExtension('WEBGL_lose_context')?.loseContext())
        } catch (error) {
            console.error('Init failed, using fallback...', error)
            setFallback(true)
            teardown()
            return
        }

        // --- Draw Loop ---
        let cancel: number
        let sized = false
        let start = false
        function tick() {
            cancel = requestAnimationFrame(tick)
            if (!start) {
                setLoading(false)
                start = true
            }
            try {
                if (!sized && video.videoWidth > 0) {
                    sized = true
                    canvas.width = video.videoWidth
                    canvas.height = Math.floor(video.videoHeight / 2)
                    gl.viewport(0, 0, canvas.width, canvas.height)
                }
                if (!sized) return
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video)
                gl.drawArrays(gl.TRIANGLES, 0, 6)
            } catch (error) {
                // bugfix for safari browsers
                console.error('Draw failed, using fallback...', error)
                setFallback(true)
                teardown()
                return
            }
        }
        tick()
        defer.push(() => cancelAnimationFrame(cancel))
        video.play().catch(() => {})

        // --- Disposal Functions ---
        function teardown() {
            let func
            while ((func = defer.shift())) {
                try {
                    func()
                } catch (error) {
                    console.error('Teardown Error:', error)
                }
            }
        }

        return teardown
    }, [])

    return (
        <div className={`media-canvas ${background ? 'background' : ''}`}>
            {!error && loading && (
                <div className="popup">
                    <img className="icon" src={vectorIconThrobber} />
                    <span className="hint">LOADING</span>
                </div>
            )}
            {error && (
                <div className="popup">
                    <img className="icon" src={vectorIconCross} />
                    <span className="hint">{error}</span>
                </div>
            )}
            {!error && fallback && (
                <img
                    className="render"
                    src={`${CDN_BASE}/${id}/standard.avif`}
                    onError={() => setError('Cannot Load Image')}
                    onLoad={() => setLoading(false)}
                />
            )}
            {!error && !fallback && (
                <>
                    <video
                        ref={videoRef}
                        crossOrigin="anonymous"
                        className="decode"
                        src={`${CDN_BASE}/${id}/alpha.webm`}
                        autoPlay
                        loop
                        muted
                        playsInline
                    />
                    <canvas ref={canvasRef} className="render" />
                </>
            )}
        </div>
    )
}
