package ballotagent

import (
	"fmt"
	"net/http"

	rad "gitlab.utc.fr/gvandevi/ia04binome2a"
	cs "gitlab.utc.fr/gvandevi/ia04binome2a/comsoc"
)

func contains[S string | rad.Ballot](s []S, e S) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func intToAlt(s []int) []cs.Alternative {
	res := make([]cs.Alternative, len(s))
	for i, v := range s {
		res[i] = cs.Alternative(v)
	}
	return res
}

func removeFromSlice(slice []string, elem string) []string {
	res := make([]string, 0)
	i := 0
	for _, el := range slice {
		if el != elem {
			res = append(res, el)
			i++
		}
	}
	return res
}

func (rsa *BallotServerAgent) receiveVote(w http.ResponseWriter, r *http.Request) {
	// mise à jour du nombre de requêtes
	rsa.Lock()
	defer rsa.Unlock()
	rsa.reqCount++

	// vérification de la méthode de la requête
	if !rsa.checkMethod("POST", w, r) {
		return
	}

	// décodage de la requête
	req, err := decodeRequest[rad.Vote](r)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// recupération des infos
	scrutin := req.BallotID
	voteBallot := rad.Ballot{BallotID: scrutin}
	ballots := make([]rad.Ballot, len(rsa.ballots))

	// Check if ballot exists
	i := 0
	for k := range rsa.ballots {
		ballots[i] = k
		i++
	}
	if !contains[rad.Ballot](ballots, voteBallot) {
		w.WriteHeader(http.StatusBadRequest)
		msg := fmt.Sprintf("The ballot '%s' does not exist", scrutin)
		w.Write([]byte(msg))
		return
	}
	ballot := rsa.ballots[voteBallot]

	// Check que la deadline n'est pas déjà passée
	if !ballot.isOpen {
		w.WriteHeader(http.StatusServiceUnavailable)
		msg := fmt.Sprintf("The deadline has passed, the ballot '%s' is therefore closed.", scrutin)
		w.Write([]byte(msg))
		return
	}

	// Check que l'agent fait bien partie des votants
	if !contains(ballot.votersId, req.AgentID) {
		w.WriteHeader(http.StatusForbidden)
		msg := fmt.Sprintf("The user '%s' has either already voted or cannot vote", req.AgentID)
		w.Write([]byte(msg))
		return
	}

	// Check if vote is valid beforehand (don't want to register a vote that could break the results' calculation)
	tempProf := make(cs.Profile, 1)
	tempProf[0] = intToAlt(req.Prefs)
	_, errPrefs := cs.MajoritySWF(tempProf)
	if errPrefs != nil || len(req.Prefs) != ballot.nbAlts {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errPrefs.Error())
		return
	}

	// Register the vote
	rsa.ballots[voteBallot] = BallotInfo{
		profile:  append(ballot.profile, intToAlt(req.Prefs)),                    // add preferences to profile
		options:  append(ballot.options, req.Options),                            // add options to all options
		votersId: removeFromSlice(rsa.ballots[voteBallot].votersId, req.AgentID), // remove voter from the list of voters
		nbAlts:   rsa.ballots[voteBallot].nbAlts,
		isOpen:   true,
		results:  rad.ResultResponse{},
	}
}