import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
import {useEffect, useReducer, useRef, useState} from "react";
import {MarchReducer} from "../../../utils/MatchReducer.jsx";
import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
import {DrawGraph} from "../../result/DrawGraph.jsx";
import {SelectCombModalContent} from "./SelectCombModalContent.jsx";

export function CategoryContent({cat, catId, setCat}) {
    const setLoading = useLoadingSwitcher()
    const {sendRequest, dispatch} = useWS();
    const [matches, reducer] = useReducer(MarchReducer, []);
    const [groups, setGroups] = useState([])
    const groupsRef = useRef(groups);
    const combDispatch = useCombsDispatch();

    useEffect(() => {
        groupsRef.current = groups
    }, [groups]);

    function readAndConvertMatch(matches, data, combsToAdd) {
        matches.push({...data, c1: data.c1?.id, c2: data.c2?.id})
        if (data.c1)
            combsToAdd.push(data.c1)
        if (data.c2)
            combsToAdd.push(data.c2)
    }

    useEffect(() => {
        const treeListener = ({data}) => {
            if (!cat || data.length < 1 || data[0].categorie !== cat.id)
                return
            setCat({
                ...cat,
                trees: data.map(d => from_sendTree(d, true))
            })

            let matches2 = [];
            let combsToAdd = [];
            data.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
            reducer({type: 'REPLACE_TREE', payload: matches2});
            combDispatch({type: 'SET_ALL', payload: {source: "match", data: combsToAdd}});
        }

        const matchListener = ({data}) => {
            reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}});
            combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}});

            if (data.c1 !== null && !groupsRef.current.some(g => g.id === data.c1?.id))
                setGroups(prev => [...prev, {id: data.c1?.id, poule: data.poule}])
            if (data.c2 !== null && !groupsRef.current.some(g => g.id === data.c2?.id))
                setGroups(prev => [...prev, {id: data.c2?.id, poule: data.poule}])
        }

        dispatch({type: 'addListener', payload: {callback: treeListener, code: 'sendTreeCategory'}})
        dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
        return () => {
            dispatch({type: 'removeListener', payload: treeListener})
            dispatch({type: 'removeListener', payload: matchListener})
        }
    }, [cat]);

    useEffect(() => {
        if (!catId)
            return;
        setLoading(1);
        sendRequest('getFullCategory', catId)
            .then((data) => {
                setCat({
                    id: data.id,
                    name: data.name,
                    liceName: data.liceName,
                    type: data.type,
                    trees: data.trees.map(d => from_sendTree(d, true))
                })

                let matches2 = [];
                let combsToAdd = [];
                data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
                data.matches.forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));

                reducer({type: 'REPLACE_ALL', payload: matches2});
                combDispatch({type: 'SET_ALL', payload: {source: "match", data: combsToAdd}});

                const activeMatches = matches2.filter(m => m.poule !== '-')
                const combsIDs = activeMatches.flatMap(d => [d.c1, d.c2]).filter((v, i, a) => v != null && a.indexOf(v) === i)
                    .map(d => {
                        return {id: d, poule: activeMatches.find(m => m.c1 === d || m.c2 === d)?.poule}
                    })
                setGroups(combsIDs)
            }).finally(() => setLoading(0))
    }, [catId]);


    return <>
        <div className="col-md-3">
            <AddComb groups={groups} setGroups={setGroups}/>
        </div>
        <div className="col-md-9">
            {cat && <ListMatch cat={cat} matches={matches} groups={groups}/>}
        </div>
    </>
}

function AddComb({groups, setGroups}) {
    const {data} = useRequestWS("getRegister", null)
    const combDispatch = useCombsDispatch();

    useEffect(() => {
        if (data === null)
            return;
        combDispatch({type: 'SET_ALL', payload: {source: "register", data: data}});
    }, [data]);

    return <>
        <ol className="list-group list-group-numbered">
            {groups.map((comb) => (
                <li key={comb.id} className="list-group-item list-group-item-action d-flex justify-content-between align-items-start">
                    <div className="ms-2 me-auto"><CombName combId={comb.id}/></div>
                    <span className="badge text-bg-primary rounded-pill">{comb.poule}</span>
                </li>)
            )}
        </ol>
        <button type="button" className="btn btn-primary mt-3 w-100" data-bs-toggle="modal" data-bs-target="#selectCombModal"
                disabled={data === null}>Ajouter des combattants
        </button>

        <div className="modal fade" id="selectCombModal" tabIndex="-1" aria-labelledby="selectCombModalLabel" aria-hidden="true">
            <div className="modal-dialog  modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
                <div className="modal-content">
                    <SelectCombModalContent data={data} setGroups={setGroups}/>
                </div>
            </div>
        </div>
    </>
}

function ListMatch({cat, matches, groups}) {
    const [type, setType] = useState(1);

    return <>
        {cat.type === 3 && <>
            <ul className="nav nav-tabs">
                <li className="nav-item">
                    <div className={"nav-link" + (type === 1 ? " active" : "")} aria-current={(type === 1 ? " page" : "false")}
                         onClick={_ => setType(1)}>Poule
                    </div>
                </li>
                <li className="nav-item">
                    <div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")}
                         onClick={_ => setType(2)}>Tournois
                    </div>
                </li>
            </ul>
        </>}

        {type === 2 && <>
            <BuildTree treeData={cat.trees} matches={matches} groups={groups}/>
        </>}
    </>
}

function BuildTree({treeData, matches, groups}) {
    const scrollRef = useRef(null)
    const selectRef = useRef(null)
    const lastMatchClick = useRef(null)
    const [combSelect, setCombSelect] = useState(0)
    const {getComb} = useCombs()
    const {sendRequest} = useWS();
    const setLoading = useLoadingSwitcher()

    function parseTree(data_in) {
        if (data_in?.data == null)
            return null

        const matchData = matches.find(m => m.id === data_in.data)
        const c1 = getComb(matchData?.c1)
        const c2 = getComb(matchData?.c2)


        let node = new TreeNode({
            ...matchData,
            c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null,
            c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null
        })
        node.left = parseTree(data_in?.left)
        node.right = parseTree(data_in?.right)

        return node
    }

    function initTree(data_in) {
        let out = []
        for (const din of data_in) {
            out.push(parseTree(din))
        }
        return out
    }

    const onMatchClick = (rect, matchId, comb) => {
        if (!treeData.some(t => t.isEnd(matchId)))
            return

        const match = matches.find(m => m.id === matchId);
        if (!match)
            return;

        const sel = selectRef.current;
        sel.style.top = rect.y + "px";
        sel.style.left = rect.x + "px";
        sel.style.width = rect.width + "px";
        sel.style.height = rect.height + "px";
        sel.style.display = "block";

        lastMatchClick.current = {matchId, comb};
        setCombSelect((comb === 1 ? match.c1 : match.c2) || 0);
    }

    const onClickVoid = () => {
        const sel = selectRef.current;
        sel.style.display = "none";

        lastMatchClick.current = null;
    }

    useEffect(() => {
        if (lastMatchClick.current == null)
            return

        const {matchId, comb} = lastMatchClick.current
        const match = matches.find(m => m.id === matchId)
        if (!match)
            return

        const combSelect_ = combSelect === 0 ? null : combSelect;

        const data = {id: match.id, c1: match.c1, c2: match.c2}
        if (comb === 1) {
            if (match.c1 === combSelect_)
                return
            data.c1 = combSelect_
        } else if (comb === 2) {
            if (match.c2 === combSelect_)
                return
            data.c2 = combSelect_
        }

        setLoading(1)
        sendRequest('updateMatchComb', data)
            .finally(() => {
                setLoading(0)
                onClickVoid()
            })
    }, [combSelect])

    const combsIDs = groups.map(m => m.id);

    return <div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}>
        <DrawGraph root={initTree(treeData)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid}/>
        <select ref={selectRef} className="form-select" style={{position: "absolute", top: 0, left: 0, display: "none"}}
                value={combSelect} onChange={e => setCombSelect(Number(e.target.value))}>
            <option value={0}>-- Sélectionner un combattant --</option>
            {combsIDs.map((combId) => (
                <option key={combId} value={combId}><CombName combId={combId}/></option>
            ))}
        </select>
    </div>
}
