package de.towerdefence.server.match;

import de.towerdefence.server.match.callbacks.PlayerHitpointsCallback;
import de.towerdefence.server.match.callbacks.PlayerMoneyCallback;
import de.towerdefence.server.match.exeptions.InvalidPlacementException;
import de.towerdefence.server.match.exeptions.InvalidPlacementReason;
import de.towerdefence.server.match.exeptions.NotInMatchException;
import de.towerdefence.server.player.Player;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.*;

@Service
public class MatchService {
    private final Map<Player, Match> playerMatches = new HashMap<>();
    private final Map<Match, ScheduledFuture<?>> moneyTasks = new HashMap<>();
    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    public void createMatch(String matchId, Player player1, Player player2) {
        Match match = new Match(matchId, player1, player2);
        playerMatches.put(player1, match);
        playerMatches.put(player2, match);
    }

    public Match get(Player player) {
        return playerMatches.get(player);
    }

    /**
     * @return opponent
     */
    public Player placeTower(Player player, int x, int y) throws NotInMatchException, InvalidPlacementException {
        Match match = playerMatches.get(player);
        if (!match.hasMatchStarted()) {
            throw new InvalidPlacementException(InvalidPlacementReason.MATCH_NOT_STARTED);
        }
        Optional<GameSession> playerSession = match.getPlayerGameSession(player);
        if (playerSession.isEmpty()) {
            throw new NotInMatchException();
        }
        Optional<Player> opponent = match.getOpponent(player);
        if (opponent.isEmpty()) {
            throw new NotInMatchException();
        }
        playerSession.get().placeTower(new Tower(), x, y);
        return opponent.get();
    }

    public void playerConnected(
        Player player,
        PlayerMoneyCallback moneyCallback,
        PlayerHitpointsCallback hitpointsCallback
    ) {
        Match match = playerMatches.get(player);
        match.connectPlayer(player, moneyCallback, hitpointsCallback);
        Optional<GameSession> optionalPlayerSession = match.getPlayerGameSession(player);
        if (optionalPlayerSession.isEmpty()) {
            return;
        }
        GameSession playerSession = optionalPlayerSession.get();
        moneyCallback.call(player, playerSession.getMoney());
        hitpointsCallback.call(player, playerSession.getPlayerHitpoints() );
        if (!match.hasMatchStarted()) {
            return;
        }
        Optional<Player> optionalOpponent = match.getOpponent(player);
        if (optionalOpponent.isEmpty()) {
            return;
        }
        Player opponent = optionalOpponent.get();
        Optional<GameSession> optionalOpponentSession = match.getPlayerGameSession(opponent);
        if (optionalOpponentSession.isEmpty()) {
            return;
        }
        GameSession opponentSession = optionalOpponentSession.get();
        ScheduledFuture<?> moneyTask = scheduler.scheduleAtFixedRate(
                () -> {
                    playerSession.addMoney(3);
                    opponentSession.addMoney(3);
                },
                5,
                5,
                TimeUnit.SECONDS);
        moneyTasks.put(match, moneyTask);
    }
}