/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'

import { useEffect, useRef, useState } from "react"

import { Device } from 'mediasoup-client'
import { Transport } from "mediasoup-client/types"

const wsURL = 'ws://localhost:4000'
let remoteStream
export default function MediaSoupWidget() {

    const socket = useRef<WebSocket | undefined>(undefined)
    const device = useRef<Device | undefined>(undefined)
    const videoRef = useRef<HTMLVideoElement | null>(null)
    const videoBRef = useRef<HTMLVideoElement | null>(null)
    const consumerTransport = useRef<Transport | null>(null)
    const [connected, setConnected] = useState<boolean>(false)
    const [states, setStates] = useState<string[]>([])

    const loadDevices = async (routerCapabilities: any) => {
        try {
            device.current = new Device()
            await device.current!.load({ routerRtpCapabilities: routerCapabilities })
            // console.log(`Supported : `, device)
            setConnected(true)
        } catch (e) {
            if ((e as any).name === 'UnsupportedErro') {
                console.log(`Not Supported`)
            }
            console.error(`Faield to create device:`, e)
        }
    }

    const getUserMedia = async (transport: any, isWebCam: boolean) => {
        if (!device.current?.canProduce('video')) {
            console.error(`Cant stream video`)
            return
        }
        let stream
        try {
            stream = isWebCam ? await navigator.mediaDevices.getUserMedia({
                video: true, audio: true
            }) : await navigator.mediaDevices.getDisplayMedia({ video: true })
        } catch (e) {
            console.error(`Unable to get device to stream`, e)
        }
        return stream
    }

    const onProducerTransportCreated = async (producerDetails: any) => {
        // 5. Createe Send Transport

        setStates(s => [...s, 'createSendTransport'])
        const transport = device.current?.createSendTransport({ ...producerDetails })
        transport?.on('connect', async ({ dtlsParameters }, callback, errback) => {
            // 6. connectProducerTransport
            setStates(s => [...s, 'createSendTransport'])
            socket.current?.send(JSON.stringify({
                type: 'connectProducerTransport',
                dtlsParameters
            }))

            socket.current?.addEventListener('message', ev => {
                // 7. producerTransportConnected
                // console.log(`SEOM TO CHK `, ev.data)
                if (JSON.parse(ev.data).type === 'producerTransportConnected') {
                    setStates(s => [...s, 'producerTransportConnected'])
                    // console.log(`------->producerTransportConnected:onCB`)
                    callback()
                }
            })
        })
        transport?.on('produce', async ({ kind, rtpParameters }, callback, errback) => {
            // 8. Produce
            setStates(s => [...s, 'produce'])
            socket.current?.send(JSON.stringify({
                type: 'produce',
                transportId: transport.id,
                kind,
                rtpParameters
            }))
            socket.current?.addEventListener('message', ev => {
                if (ev.data.type === 'published') {
                    callback(ev.data.id``)
                }
            })
        })

        transport?.on('connectionstatechange', async (state) => {
            console.log(`connectionstatechange`, state)
            switch (state) {
                case 'connecting': break;
                case 'closed': break;
                case 'connected':
                    // Link stream here    
                    break;
                case 'disconnected': break;
                case 'failed':
                    transport.close()
                    break;
                case 'new': break;
            }
        })
        transport?.on('icecandidateerror', async (error) => {
            console.log('icecandidateerror', error)
        })
        transport?.on('icegatheringstatechange', async (error) => {
            console.log('icegatheringstatechange', error)
        })
        transport?.on('producedata', async (error) => {
            console.log('producedata', error)
        })

        let streamMedia
        try {
            streamMedia = await getUserMedia(transport, true) // is webcam
            const track = streamMedia!.getVideoTracks()[0]
            // const params - { track }
            // videoRef.current?.h = track
            console.log(`LocalStream`, streamMedia)
            // videoRef.current!.srcObject = streamMedia

            const producer = await transport?.produce({ track })
            producer!.on("trackended", () => {
                console.log("track ended");
            });
            producer!.on("transportclose", () => {
                console.log("transport ended");
            });
        }
        catch (e) {
            console.log(`ERROR`, e)
        }

    }

    const onSubTransportCreated = (consumerDetails: any) => {
        consumerTransport.current = device.current!.createRecvTransport({ ...consumerDetails })

        console.log(`onSubTransportCreated`, consumerDetails, consumerTransport.current.connectionState)
        consumerTransport.current.on('connect', ({ dtlsParameters }, callback, errback) => {
            //11 . Accept connect

            setStates(s => [...s, 'connectConsumerTransport'])
            socket.current?.send(JSON.stringify({
                type: 'connectConsumerTransport',
                transportId: consumerTransport.current!.id,
                dtlsParameters
            }))
            socket.current?.addEventListener('message', ev => {
                if (JSON.parse(ev.data).type === 'subConnected') {
                    console.log(`subConnected**`, JSON.parse(ev.data).type)
                    callback()
                }
            })
        })
        consumerTransport.current?.on('connectionstatechange', async (state) => {
            console.log(`connectionstatechange:`, state)
            switch (state) {
                case 'connecting':

                    break;
                case 'failed': console.log(`FILAED`); break;
                case 'connected':
                    console.log(`remoteStream`, remoteStream)
                    // videoBRef.current!.srcObject = remoteStream

                    socket.current?.send(JSON.stringify({
                        type: 'resume',
                    }))
                    break;
                default:
                    break;
            }
        })
        consumerTransport.current?.on('icecandidateerror', () => {
            console.log(`icecandidateerror`)
        })
        consumerTransport.current?.on('icegatheringstatechange', async (state) => {
            console.log(`icegatheringstatechange`, state)
        })

        const stream = consumer(consumerTransport.current)
    }

    const onSubscribe = async (details: any) => {
        console.log('onSubscribe', details)
        const {
            producerId,
            id,
            kind,
            rtpParameters,
            type,
            producerPaused,
        } = details

        const codecOptions = {}
        const consumer = await consumerTransport.current!.consume({
            producerId,
            id,
            kind,
            rtpParameters,
            // type,
            // producerPaused,
            // codecOptions
        })
        const stream = new MediaStream()
        stream.addTrack(consumer.track)
        console.log(`TRYIN STREAM 1`,stream)
        videoBRef.current!.srcObject = stream
    }

    const consumer = async (transport: any) => {
        const rtpCapabilities = device.current?.recvRtpCapabilities
        socket.current?.send(JSON.stringify({
            type: 'consume',
            rtpCapabilities
        }))
    }
    // const connectSendTransport = async () => {
    //     const producer = await transport.produce(params);
    //     console.log("Producer created:", producer.id, producer.kind);
    //     producer.on("trackended", () => {
    //         console.log("track ended");
    //     });
    //     producer.on("transportclose", () => {
    //         console.log("transport ended");
    //     });
    //     };

    const stream = () => {
        // 4.1 Start creating producers steam

        // 4. Producers stream
        setStates(s => [...s, 'createProducerTransport'])
        socket.current?.send(JSON.stringify({
            type: 'createProducerTransport',
            forceTcp: false,
            rtpCapabilities: device.current?.sendRtpCapabilities
        }))
    }


    const joinStream = () => {
        setStates(s => [...s, 'createConsumerTransport'])
        socket.current?.send(JSON.stringify({
            type: 'createConsumerTransport',
            forceTcp: false,
            // rtpCapabilities: device.current?.sendRtpCapabilities
        }))
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const parseWSMessage = (ev: any) => {
        const recv = JSON.parse(ev)
        // console.log(`-- parseWSMessage --`, recv.data)
        switch (recv.type) {
            case 'routerCapabilities':
                // 3. Received capabilities
                setStates(s => [...s, 'routerCapabilities'])
                loadDevices(recv.data);
                break;
            case 'producerTransportCreated':
                // 4.2 Received capabilities
                setStates(s => [...s, 'producerTransportCreated'])
                onProducerTransportCreated(recv.data);
                break;
            // case 'producerTransportConnected': 
            // // 7. producerTransportConnected but not with callback
            //     setStates(s => [...s, 'producerTransportCreated'])
            // // callback()
            // break;
            case 'newProducer':
                // 9 Found new Produce contents and send to all clients
                setStates(s => [...s, 'newProducer'])
                break;
            case 'subTransportCreated':
                // 10 Consumer joined 
                setStates(s => [...s, 'subTransportCreated'])
                onSubTransportCreated(recv.data);
                break;
            case 'resumed':
                // 10 Consumer joined 
                setStates(s => [...s, 'resumed'])
                console.log(`resumed`, recv.data)
                break;
            case 'subscribed':
                // 12 Consumer joined 
                setStates(s => [...s, 'subscribed'])
                onSubscribe(recv.data)
                break;
            default:
                console.log(`Received Uknown`, recv.type)

                break;
        }
        // console.log(ev)

    }
    return (
        <div>WebRTCChat
            <div>
                <div className={`${states.includes('ws-connected') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Connected To Server</div>
                    <small>Establish connection to the WS Server</small>
                </div>
                <div className={`${states.includes('getRouterRtpCapabilities') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Checking Server Capabilities</div>
                    <small>getRouterRtpCapabilities</small>
                </div>
                <div className={`${states.includes('routerCapabilities') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Has Server Capabilities</div>
                    <small>routerCapabilities</small>
                </div>
                <div className={`${states.includes('createProducerTransport') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Create Producers Transport (Waiting for user to start stream)</div>
                    <small>createProducerTransport</small>
                </div>
                <div className={`${states.includes('producerTransportCreated') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Created Producers Transport</div>
                    <small>producerTransportCreated</small>
                </div>
                <div className={`${states.includes('producerTransportCreated') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Created Producers Transport</div>
                    <small>producerTransportCreated</small>
                </div>
                <div className={`${states.includes('createSendTransport') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Created Send Transport</div>
                    <small>createSendTransport</small>
                </div>
                <div className={`${states.includes('producerTransportConnected') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Producer Transport Connected</div>
                    <small>producerTransportConnected</small>
                </div>
                <div className={`${states.includes('producerTransportConnected') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Send Produced</div>
                    <small>produce</small>
                </div>
                <div className={`${states.includes('newProducer') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Found new Produce contents and send to all clients</div>
                    <small>newProducer</small>
                </div>
                <div className={`${states.includes('subTransportCreated') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Cunsumber connected</div>
                    <small>subTransportCreated</small>
                </div>
                <div className={`${states.includes('connectConsumerTransport') ? 'text-green-400' : 'text-gray-400'}  border`}>
                    <div>Accept Consumer Connection</div>
                    <small>connectConsumerTransport</small>
                </div>
            </div>
            <button
                onClick={() => {
                    socket.current = new WebSocket(wsURL)
                    socket.current.onopen = () => {
                        // 1. Create websocket connection
                        setStates(s => [...s, 'ws-connected'])
                        const msg = {
                            type: "getRouterRtpCapabilities"
                        }
                        try {
                            // 2. Get Server capabilities
                            setStates(s => [...s, 'getRouterRtpCapabilities'])
                            socket.current?.send(JSON.stringify(msg))
                        } catch (e) {
                            console.log(`Failed to send capabilities`, e)
                        }
                    }
                    socket.current.onmessage = e => parseWSMessage((e as any).data)
                }}
                disabled={connected}
            >{connected ? 'Connected' : 'Connect'}</button>
            <button onClick={() => stream()}>
                Stream
            </button>
            <video ref={videoRef}></video>
            <button
                onClick={() => {
                    joinStream()
                }}

            >Join</button>
            <video ref={videoBRef}></video>

        </div>
    )
}
