import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
import React, {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";
import {createMatch, scoreToString2} from "../../../utils/CompetitionTools.js";

import {DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors} from '@dnd-kit/core';
import {SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy} from '@dnd-kit/sortable';
import {useSortable} from '@dnd-kit/sortable';
import {CSS} from '@dnd-kit/utilities';
import {toast} from "react-toastify";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faPen, faTrash} from "@fortawesome/free-solid-svg-icons";
import {getToastMessage, virtualScore, win_end} from "../../../utils/Tools.js";
import {useTranslation} from "react-i18next";
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";

import {ScorePanel} from "./ScoreAndCardPanel.jsx";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
import {AutoCatModalContent} from "../../../components/cm/AutoCatModalContent.jsx";

const vite_url = import.meta.env.VITE_URL;

function CupImg() {
    return <img decoding="async" loading="lazy" width={"16"} height={"16"} className="wp-image-1635"
                style={{width: "16px"}} src="/img/171891.png"
                alt=""/>
}

function CupImg2() {
    return <img decoding="async" loading="lazy" width={"16"} height={"16"} className="wp-image-1635"
                style={{width: "16px"}} src="/img/171892.png"
                alt=""/>
}

export function CategoryContent({cat, catId, setCat, menuActions}) {
    const setLoading = useLoadingSwitcher()
    const {sendRequest, dispatch} = useWS();
    const cardDispatch = useCardsDispatch();
    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,
            c1_cacheName: data.c1?.fname + " " + data.c1?.lname,
            c2_cacheName: data.c2?.fname + " " + data.c2?.lname
        })
        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_ => ({
                ...cat_,
                trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)),
                raw_trees: data.sort((a, b) => a.level - b.level)
            }))

            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: datas}) => {
            for (const data of datas) {
                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.categorie !== cat.id)
                    continue;

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

        const matchOrder = ({data}) => {
            reducer({type: 'REORDER', payload: data});
        }

        const deleteMatch = ({data: datas}) => {
            for (const data of datas)
                reducer({type: 'REMOVE', payload: data});
        }

        dispatch({type: 'addListener', payload: {callback: treeListener, code: 'sendTreeCategory'}})
        dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
        dispatch({type: 'addListener', payload: {callback: matchOrder, code: 'sendMatchOrder'}})
        dispatch({type: 'addListener', payload: {callback: deleteMatch, code: 'sendDeleteMatch'}})
        return () => {
            dispatch({type: 'removeListener', payload: treeListener})
            dispatch({type: 'removeListener', payload: matchListener})
            dispatch({type: 'removeListener', payload: matchOrder})
            dispatch({type: 'removeListener', payload: deleteMatch})
        }
    }, [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.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)),
                    raw_trees: data.trees.sort((a, b) => a.level - b.level),
                    treeAreClassement: data.treeAreClassement,
                    fullClassement: data.fullClassement,
                    preset: data.preset,
                })

                cardDispatch({type: 'SET_ALL', payload: data.cards});

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

    const removeGroup = (combId) => {
        if (matches.some(d => d.c1 === combId || d.c2 === combId)) {
            setGroups(prev => prev.map(g => {
                if (g.id === combId)
                    return {...g, poule: "-"}
                return g;
            }))
        } else {
            setGroups(prev => prev.filter(g => g.id !== combId))
        }
    }


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

function PrintMatch({menuActions, cat, groups, matches}) {
    const {cards_v} = useCards();
    const marches2 = matches.filter(m => m.categorie === cat.id)
        .map(m => ({...m, ...win_end(m, cards_v)}))

    marches2.forEach(m => {
        if (m.end && (!m.scores || m.scores.length === 0))
            m.scores = [{n_round: 0, s1: 0, s2: 0}];
    })

    menuActions.printCategorie = (categorieEmpty) => {
        return [cat.name, [
            {type: "categorie", params: ({cat, matches: marches2, groups, cards_v, categorieEmpty})}
        ]]
    }
    return <></>
}

function AddComb({groups, setGroups, removeGroup, menuActions, cat}) {
    const {data, setData} = useRequestWS("getRegister", null)
    const combDispatch = useCombsDispatch()
    const {dispatch} = useWS()
    const [modalId, setModalId] = useState(null)
    const [modalMode, setModalMode] = useState(false)
    const {t} = useTranslation("cm");

    useEffect(() => {
        const sendRegister = ({data}) => {
            setData(prev => {
                if (prev === null)
                    return [data];
                return [...prev.filter(d => d.id !== data.id), data];
            })
        }
        const sendRegisterRemove = ({data}) => {
            setData(prev => {
                if (prev === null)
                    return null;
                return [...prev.filter(d => d.id !== data)];
            })
        }

        dispatch({type: 'addListener', payload: {callback: sendRegister, code: 'sendRegister'}})
        dispatch({type: 'addListener', payload: {callback: sendRegisterRemove, code: 'sendRegisterRemove'}})
        return () => {
            dispatch({type: 'removeListener', payload: {callback: sendRegister, code: 'sendRegister'}})
            dispatch({type: 'removeListener', payload: {callback: sendRegisterRemove, code: 'sendRegisterRemove'}})
        }
    }, []);

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

        const resourceList = []
        data.forEach(d => {
            if (d.club_uuid) {
                const url = `${vite_url}/api/club/${d.club_uuid}/logo`;
                if (!resourceList.some(d => d.url === url))
                    resourceList.push({url: url, name: `club_${d.club_uuid}.png`});
            }
            if (d.country) {
                const url = `/flags/svg/${d.country.toLowerCase()}.svg`;
                if (!resourceList.some(d => d.url === url))
                    resourceList.push({url: url, name: `flag_${d.country.toLowerCase()}.svg`});
            }
        })
        menuActions.current.resourceList = resourceList
    }, [data]);

    return <>
        <GroupsList groups={groups} setModalId={setModalId}/>
        <button type="button" className="btn btn-primary mt-3 w-100" data-bs-toggle="modal" data-bs-target="#selectCombModal"
                disabled={data === null} onClick={() => setModalMode(false)}>{t('ajouterDesCombattants')}
        </button>

        <button type="button" className="btn btn-primary mt-3 w-100" data-bs-toggle="modal" data-bs-target="#selectCombModal"
                disabled={data === null} onClick={() => setModalMode(true)}>{t('ajouterUneTeam')}
        </button>

        <button type="button" className="btn btn-primary mt-3 w-100" data-bs-toggle="modal" data-bs-target="#autoCatModal"
                disabled={data === null}>{t('ajoutAutomatique')}
        </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} teamMode={modalMode} groups={groups}
                                            defaultPreset={cat?.preset?.id || -1}/>
                </div>
            </div>
        </div>

        <div className="modal fade" id="autoCatModal" tabIndex="-1" aria-labelledby="autoCatModalLabel" aria-hidden="true">
            <div className="modal-dialog  modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
                <div className="modal-content">
                    <AutoCatModalContent data={data} groups={groups} setGroups={setGroups} defaultPreset={cat?.preset?.id || -1}/>
                </div>
            </div>
        </div>

        <div className="modal fade" id="groupeModal" tabIndex="-1">
            <div className="modal-dialog">
                <div className="modal-content">
                    <GroupModalContent combId={modalId} groups={groups} setGroups={setGroups} removeGroup={removeGroup}/>
                </div>
            </div>
        </div>
    </>
}

function GroupsList({groups, setModalId}) {
    const {getComb} = useCombs();
    const {t} = useTranslation("cm");

    const groups2 = groups.map(g => {
        const comb = getComb(g.id);
        return {...g, name: comb ? comb.fname + " " + comb.lname : "", teamMembers: comb ? comb.teamMembers : []};
    }).sort((a, b) => {
        if (a.poule !== b.poule) {
            if (a.poule === '-') return 1;
            if (b.poule === '-') return -1;
            return a.poule.localeCompare(b.poule);
        }
        return a.name.localeCompare(b.name);
    }).reduce((acc, curr) => {
        const poule = curr.poule;
        if (!acc[poule]) {
            acc[poule] = [];
        }
        acc[poule].push(curr);
        return acc;
    }, {});

    return <>
        {Object.keys(groups2).map((poule) => (
            <div key={poule} className="mb-3">
                <h5>{poule !== '-' ? (t('poule') + " : " + poule) : t('sansPoule')}</h5>
                <ol className="list-group list-group-numbered">
                    {groups2[poule].map((comb) => (
                        <li key={comb.id} className="list-group-item list-group-item-action d-flex justify-content-between align-items-start"
                            data-bs-toggle="modal" data-bs-target="#groupeModal" onClick={_ => setModalId(comb.id)}>
                            <div className="ms-2 me-auto">
                                <CombName combId={comb.id}/>
                                {comb.teamMembers.length > 0 && <>
                                    {comb.teamMembers.map((m) => (<small key={m.id}>
                                        <br/>
                                        <CombName combId={m.id}/>
                                    </small>))}
                                </>}
                            </div>
                            <span className="badge text-bg-primary rounded-pill">{comb.poule}</span>
                        </li>)
                    )}
                </ol>
            </div>
        ))}
    </>
}

function GroupModalContent({combId, groups, setGroups, removeGroup}) {
    const inputRef = useRef(null);
    const [pouleInput, setPouleInput] = useState(groups.find(g => g.id === combId)?.poule || "")
    const {t} = useTranslation("cm");

    useEffect(() => {
        setPouleInput(groups.find(g => g.id === combId)?.poule || "")
    }, [combId]);

    const handleClick = () => {
        if (pouleInput.trim().length !== 1 || pouleInput === "-")
            return;

        setGroups(prev => prev.map(g => {
            if (g.id === combId)
                return {...g, poule: pouleInput}
            return g;
        }))
        // close modal
        const modal = document.getElementById("groupeModal");
        const modalInstance = window.bootstrap.Modal.getInstance(modal);
        modalInstance.hide();
    }

    if (inputRef.current) {
        setTimeout(() => {
            inputRef.current.focus()
            inputRef.current.select()
        }, 500);
    }

    return <>
        <div className="modal-header">
            <h5 className="modal-title">{t('poulePour')}<CombName combId={combId}/></h5>
            <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div className="modal-body">
            <input ref={inputRef} type="text" className="form-control" value={pouleInput} maxLength="1" onChange={e => setPouleInput(e.target.value)}
                   onKeyDown={event => {
                       if (event.key === 'Enter')
                           handleClick();
                   }}/>
        </div>
        <div className="modal-footer">
            <button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
            <button type="button" className="btn btn-primary" onClick={handleClick}>{t('enregister')}</button>
            <button type="button" className="btn btn-danger" data-bs-dismiss="modal" onClick={() => {
                removeGroup(combId)
            }}>{t('supprimer')}
            </button>
        </div>
    </>

}

function ListMatch({cat, matches, groups, reducer}) {
    const {sendRequest} = useWS();
    const [type, setType] = useState(1);
    const bthRef = useRef(null);
    const bthRef2 = useRef(null);
    const {t} = useTranslation("cm");

    useEffect(() => {
        if ((cat.type & type) === 0)
            setType(cat.type);
    }, [cat]);

    const handleCreatMatch = () => {
        if (matches.some(m => m.poule !== '-')) {
            bthRef.current.click();
            return;
        }
        recalculateMatch(0);
    }

    const handleCreatClassement_ = () => {
        toast.promise(sendRequest("createClassementMatchs", cat.id), getToastMessage("toast.matchs.classement.create", "cm"))
    }
    const handleCreatClassement = () => {
        if (matches.some(m => m.categorie === cat.id && m.categorie_ord === -42 && m.c1 !== undefined && m.c2 !== undefined)) {
            bthRef2.current.click();
            return;
        }
        handleCreatClassement_()
    }

    const recalculateMatch = (mode) => {
        let matchesToKeep = [];
        let matchesToRemove = [];
        if (mode === 2) {
            matchesToKeep = matches.filter(m => m.categorie === cat.id && m.categorie_ord !== -42);
        } else if (mode === 1) {
            matchesToKeep = matches.filter(m => m.categorie === cat.id && m.end === true && m.categorie_ord !== -42)
            matchesToRemove = matches.filter(m => m.categorie === cat.id && m.end === false && m.categorie_ord !== -42)
        } else {
            matchesToRemove = matches.filter(m => m.categorie === cat.id && m.categorie_ord !== -42)
        }

        try {
            const {newMatch, matchOrderToUpdate, matchPouleToUpdate} = createMatch(cat, matchesToKeep, groups.filter(g => g.poule !== '-'))

            toast.promise(sendRequest("recalculateMatch", {
                categorie: cat.id,
                newMatch,
                matchOrderToUpdate: Object.fromEntries(matchOrderToUpdate),
                matchPouleToUpdate: Object.fromEntries(matchPouleToUpdate),
                matchesToRemove: matchesToRemove.map(m => m.id)
            }), getToastMessage("toast.matchs.create", "cm"))
                .finally(() => {
                    console.log("Finished creating matches");
                })

        } catch (e) {
            toast.error(t('erreurLorsDeLaCréationDesMatchs') + e.message);
        }
    }

    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)}>{t('poule')}
                    </div>
                </li>
                <li className="nav-item">
                    <div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")}
                         onClick={_ => setType(2)}>{cat.treeAreClassement ? t('classement') : t('tournois')}
                    </div>
                </li>
            </ul>
        </>}

        {type === 1 && <>
            <MatchList matches={matches} groups={groups} cat={cat} reducer={reducer}/>
            <button className="btn btn-primary float-end" onClick={handleCreatMatch}>{t('créerLesMatchs')}</button>
        </>}

        {type === 2 && <>
            <BuildTree treeData={cat.trees} treeRaw={cat.raw_trees} cat={cat} matches={matches} groups={groups}/>
            {cat.treeAreClassement &&
                <button className="btn btn-primary float-end" onClick={handleCreatClassement}>{t('créerLesMatchesDeClassement')}</button>}
        </>}

        <button ref={bthRef} data-bs-toggle="modal" data-bs-target="#makeMatchMode" style={{display: "none"}}>open</button>
        <div className="modal fade" id="makeMatchMode" tabIndex="-1" role="dialog" aria-labelledby="makeMatchModeLabel" aria-hidden="true">
            <div className="modal-dialog">
                <div className="modal-content">
                    <div className="modal-header">
                        <h4 className="modal-title" id="makeMatchModeLabel">{t('attention')}</h4>
                    </div>
                    <div className="modal-body">
                        {t('msg1')}
                    </div>
                    <div className="modal-footer">
                        <div className="btn-group" role="group" aria-label="Basic mixed styles example">
                            <button className="btn btn-primary" data-dismiss="modal" data-bs-dismiss="modal" onClick={() => recalculateMatch(2)}>
                                {t('toutConserver')}
                            </button>
                            <button className="btn btn-success" data-bs-dismiss="modal" onClick={() => recalculateMatch(1)}>
                                {t('conserverUniquementLesMatchsTerminés')}
                            </button>
                            <button className="btn btn-danger" data-bs-dismiss="modal" onClick={() => recalculateMatch(0)}>
                                {t('neRienConserver')}
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <button ref={bthRef2} data-bs-toggle="modal" data-bs-target="#confirm-regenerate-classement" style={{display: "none"}}>open</button>
        <ConfirmDialog id="confirm-regenerate-classement"
                       title={t('créerLesMatchesDeClassement')}
                       message={t('créerLesMatchesDeClassement.msg')}
                       onConfirm={handleCreatClassement_}/>
    </>
}

function MatchList({matches, cat, groups, reducer, classement = false}) {
    const {sendRequest} = useWS();
    const setLoading = useLoadingSwitcher()
    const selectRef = useRef(null)
    const tableRef = useRef(null)
    const lastMatchClick = useRef(null)
    const [combSelect, setCombSelect] = useState(0)
    const [combC1nm, setCombC1nm] = useState(null)
    const [combC2nm, setCombC2nm] = useState(null)
    const [modalMatchId, setModalMatchId] = useState(null)
    const {t} = useTranslation("cm");
    const {cards_v, getHeightCardForCombInMatch} = useCards();
    const matchModal = useRef(null);

    const liceName = (cat.liceName || "N/A").split(";");
    const marches2 = classement
        ? matches.filter(m => m.categorie_ord === -42 && m.categorie === cat.id)
            .map(m => ({...m, ...win_end(m, cards_v)}))
        : matches.filter(m => m.categorie_ord !== -42 && m.categorie === cat.id)
            .sort((a, b) => a.categorie_ord - b.categorie_ord)
            .map(m => ({...m, ...win_end(m, cards_v)}))

    marches2.forEach(m => {
        if (m.end && (!m.scores || m.scores.length === 0))
            m.scores = [{n_round: 0, s1: 0, s2: 0}];
    })

    const sensors = useSensors(
        useSensor(PointerSensor),
        useSensor(KeyboardSensor, {
            coordinateGetter: sortableKeyboardCoordinates,
        })
    );

    const handleDragEnd = async (event) => {
        if (classement)
            return;

        const {active, over} = event;
        if (active.id !== over.id) {
            let newIndex = marches2.findIndex(m => m.id === over.id);
            if (newIndex > 0)
                newIndex = marches2[newIndex].categorie_ord;
            reducer({type: 'REORDER', payload: {id: active.id, pos: newIndex}});
            sendRequest('updateMatchOrder', {id: active.id, pos: newIndex}).then(__ => {
            })
        }
    };

    useEffect(() => {
        if (!combC1nm || !combC2nm)
            return;
        if (combC1nm === combC2nm) {
            toast.error(t('err1'));
            return;
        }

        setLoading(1)
        sendRequest('addMatch', {
            categorie: cat.id,
            categorie_ord: Math.max(...marches2.map(o => o.categorie_ord)) + 1,
            poule: groups.find(g => g.id === combC1nm).poule,
            c1: combC1nm,
            c2: combC2nm
        }).then(_ => {
            setCombC1nm(null)
            setCombC2nm(null)
        }).finally(() => setLoading(0))

    }, [combC1nm, combC2nm]);

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

        const {matchId, combId} = lastMatchClick.current
        if (matchId === -1) {
            setCombC1nm(combSelect === 0 ? null : combSelect);
            onClickVoid()
            return
        }
        if (matchId === -2) {
            setCombC2nm(combSelect === 0 ? null : combSelect);
            onClickVoid()
            return
        }

        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 (match.c1 === combId) {
            if (match.c1 === combSelect_)
                return
            data.c1 = combSelect_
        } else if (match.c2 === combId) {
            if (match.c2 === combSelect_)
                return
            data.c2 = combSelect_
        }

        if (data.c1 === data.c2) {
            toast.error(t('err1'));
            onClickVoid()
            return;
        }

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

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

        if (!confirm(t('confirm1')))
            return;

        setLoading(1)
        sendRequest('deleteMatch', matchId)
            .finally(() => setLoading(0))
    }

    const handleEditMatch = (matchId) => {
        const match = matches.find(m => m.id === matchId)
        if (!match)
            return;
        setModalMatchId(matchId);
        matchModal.current.click();
    }

    const handleCombClick = (e, matchId, combId) => {
        e.stopPropagation();
        const tableRect = tableRef.current.getBoundingClientRect();
        const rect = e.currentTarget.getBoundingClientRect();

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

        setCombSelect(combId || 0);
        lastMatchClick.current = {matchId, combId};
    }

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

    const GetCard = ({combId, match, cat}) => {
        const c = getHeightCardForCombInMatch(combId, match)
        if (!c)
            return <></>
        let bg = "";
        switch (c.type) {
            case "YELLOW":
                bg = " bg-warning";
                break;
            case "RED":
                bg = " bg-danger";
                break;
            case "BLACK":
                bg = " bg-dark text-white";
                break;
            case "BLUE":
                bg = " bg-primary text-white";
                break;
        }
        return <span
            className={"position-absolute top-0 start-100 translate-middle-y badge border border-light p-2" + bg +
                (c.match === match.id ? " rounded-circle" : (hasEffectCard(c, match.id, cat.id) ? "" : " bg-opacity-50"))}>
                                <span className="visually-hidden">card</span></span>
    }

    const combsIDs = groups.map(m => m.id);
    return <div style={{position: "relative"}}>
        <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
            <SortableContext items={marches2.map(m => m.id)} strategy={verticalListSortingStrategy}>
                <div ref={tableRef} className="table-responsive-lg" style={{fontSize: "1rem"}} onClick={onClickVoid}>
                    <table className="table table-striped">
                        <thead>
                        <tr>
                            <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th>
                            {!classement &&
                                <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('poule')}</th>}
                            {!classement &&
                                <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('zone')}</th>}
                            <th style={{textAlign: "center"}} scope="col"></th>
                            <th style={{textAlign: "center"}} scope="col">{t('rouge')}</th>
                            <th style={{textAlign: "center"}} scope="col">{t('blue')}</th>
                            <th style={{textAlign: "center"}} scope="col"></th>
                            <th style={{textAlign: "center"}} scope="col">{t('résultat')}</th>
                            <th style={{textAlign: "center"}} scope="col"></th>
                            {!classement && <th style={{textAlign: "center"}} scope="col"></th>}
                            {!classement && <th style={{textAlign: "center"}} scope="col"></th>}
                        </tr>
                        </thead>
                        <tbody className="table-group-divider">
                        {marches2.map((m, index) => (
                            <SortableRow key={m.id} id={m.id}>
                                <th style={{textAlign: "center", cursor: "auto"}} scope="row">{index + 1}</th>
                                {!classement && <td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>}
                                {!classement && <td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>}
                                <td style={{textAlign: "right", cursor: "auto", paddingRight: "0"}}>{m.end && ((m.win > 0 &&
                                    <CupImg/>) || (m.win === 0 && <CupImg2/>))}</td>
                                <td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}
                                    onClick={e => handleCombClick(e, m.id, m.c1)}>
                                    <small className="position-relative"><CombName combId={m.c1}/>
                                        <GetCard match={m} combId={m.c1} cat={cat}/></small></td>
                                <td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}
                                    onClick={e => handleCombClick(e, m.id, m.c2)}>
                                    <small className="position-relative"><CombName combId={m.c2}/>
                                        <GetCard match={m} combId={m.c2} cat={cat}/></small></td>
                                <td style={{textAlign: "left", cursor: "auto", paddingLeft: "0"}}>{m.end && ((m.win < 0 &&
                                    <CupImg/>) || (m.win === 0 && <CupImg2/>))}</td>
                                <td style={{textAlign: "center", cursor: "auto"}}>{scoreToString2(m, cards_v)}</td>
                                <td style={{textAlign: "center", cursor: "pointer", color: "#1381ff"}} onClick={_ => handleEditMatch(m.id)}>
                                    <FontAwesomeIcon icon={faPen}/></td>
                                {!classement &&
                                    <td style={{textAlign: "center", cursor: "pointer", color: "#ff1313"}} onClick={_ => handleDelMatch(m.id)}>
                                        <FontAwesomeIcon icon={faTrash}/></td>}
                                <td style={{textAlign: "center", cursor: "grab"}} hidden={classement}>☰</td>
                            </SortableRow>
                        ))}
                        {!classement && <tr>
                            <td>-</td>
                            <td></td>
                            <td></td>
                            <td></td>
                            <td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}
                                onClick={e => handleCombClick(e, -1, combC1nm)}>
                                {combC1nm && <small><CombName combId={combC1nm}/></small>}</td>
                            <td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}
                                onClick={e => handleCombClick(e, -2, combC2nm)}>
                                {combC2nm && <small><CombName combId={combC2nm}/></small>}</td>
                            <td></td>
                            <td></td>
                            <td></td>
                            <td></td>
                            <td></td>
                        </tr>}
                        </tbody>
                    </table>
                </div>
            </SortableContext>
        </DndContext>

        <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}>{t('--SélectionnerUnCombattant--')}</option>
            {combsIDs.map((combId) => (
                <option key={combId} value={combId}><CombName combId={combId}/></option>
            ))}
        </select>

        <button ref={matchModal} type="button" style={{display: "none"}} data-bs-toggle="modal" data-bs-target="#editMatchModal">open</button>
        <div className="modal fade" id="editMatchModal" tabIndex="-1" aria-labelledby="editMatchModalLabel" aria-hidden="true">
            <div className="modal-dialog  modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
                <div className="modal-content">
                    <MatchEditModalContent matchId={modalMatchId} matches={matches}/>
                </div>
            </div>
        </div>
    </div>
}

function MatchEditModalContent({matchId, matches}) {
    const menuActionsLocal = useRef({});
    const match = matches.find(m => m.id === matchId)
    const {t} = useTranslation("cm");

    return <>
        <div className="modal-header">
            <h1 className="modal-title fs-5" id="editMatchModalLabel">{t('editionDuMatch')}</h1>
            <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div className="modal-body" style={{textAlign: "center"}}>
            <ScorePanel matchId={matchId} match={match} matchs={matches} menuActions={menuActionsLocal} admin={true}/>
        </div>
        <div className="modal-footer">
            <button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
        </div>
    </>
}

function SortableRow({id, children}) {
    const {
        attributes,
        listeners,
        setNodeRef,
        transform,
        transition,
        isDragging,
    } = useSortable({id});

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
        opacity: isDragging ? 0.5 : 1,
        touchAction: 'none', // Désactive le scroll par défaut pendant le drag
    };

    return (
        <tr ref={setNodeRef} style={style} {...attributes}>
            {React.Children.map(children, (child, i) =>
                i === children.length - 1 // Dernière cellule (icône de drag)
                    ? React.cloneElement(child, {
                        ...child.props, ...attributes, ...listeners, style: {...child.props.style, cursor: "grab", touchAction: 'none'},
                    })
                    : child
            )}
        </tr>
    );
}

function BuildTree({treeData, treeRaw, matches, cat, 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()
    const {t} = useTranslation("cm");
    const {cards_v} = useCards();

    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)

        const scores2 = []
        for (const score of matchData?.scores) {
            scores2.push({
                ...score,
                s1: virtualScore(matchData?.c1, score, matchData, cards_v),
                s2: virtualScore(matchData?.c2, score, matchData, cards_v)
            })
        }

        let node = new TreeNode({
            ...matchData,
            ...win_end(matchData, cards_v),
            scores: scores2,
            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, data_raw) {
        let out = []
        for (let i = 0; i < data_raw.length; i++) {
            if (data_raw.at(i).level > -10) {
                out.push(parseTree(data_in.at(i)))
            }
        }
        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, treeRaw)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} cards={cards_v}/>
        {cat.fullClassement &&
            <MatchList matches={treeRaw.filter(n => n.level <= -10).reverse().map(d => matches.find(m => m.id === d.match?.id))}
                       groups={groups} cat={cat} reducer={undefined} classement={true}/>}
        <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}>{t('--SélectionnerUnCombattant--')}</option>
            {combsIDs.map((combId) => (
                <option key={combId} value={combId}><CombName combId={combId}/></option>
            ))}
        </select>
    </div>
}
