package com.uca;

import com.uca.core.ExchangeCore;
import com.uca.core.PokemonCore;
import com.uca.core.UserCore;
import com.uca.dao._Initializer;
import com.uca.entity.ExchangeEntity;
import com.uca.entity.PokedexEntity;
import com.uca.entity.PokemonEntity;
import com.uca.entity.UserEntity;
import com.uca.exceptions.ServiceException;
import com.uca.gui.LandingGUI;
import com.uca.gui.LoginGUI;
import com.uca.gui.PokemonGUI;
import com.uca.gui.UserGUI;
import com.uca.gui.ExchangeGUI;
import com.uca.security.doLogin;
import spark.Request;

import java.time.LocalDate;
import java.util.Random;

import static spark.Spark.*;

public class StartServer {
    public static final int NB_POKEMONS = 1008;

    public static void main(String[] args) {
        //Configure Spark
        staticFiles.location("/static/");
        port(8081);

        _Initializer.Init(false);

        // Définition des routes

        /* Menu & Connexion */

        get("/", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            return LandingGUI.getLandingPage(connectedUser);
        });

        get("/login", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser != null) {
                res.redirect("/", 301);
                return null;
            }

            return LoginGUI.getLoginPage();
        });

        post("/login", (req, res) -> {
            if (req.queryParams("identifier") != null) {
                String username = req.queryParams("identifier");
                String password = req.queryParams("password");

                UserEntity userEntity = new UserEntity();
                userEntity.setIdentifier(username);
                userEntity.setPassword(password);

                try {
                    res.cookie("/", "auth", doLogin.login(userEntity), 36000, false, true);
                    authentificateUser(userEntity);
                } catch (Exception e) {
                    res.redirect("/login", 301);
                }
            }
            res.redirect("/", 301);
            return "";
        });

        get("/logout", (req, res) -> {
            res.removeCookie("auth");
            res.redirect("/", 301);
            return "";
        });

        get("/signin", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser != null) {
                res.redirect("/login", 301);
                return null;
            }

            return LoginGUI.getSigninPage();
        });

        post("/signin", (req, res) -> {
            if (req.queryParams("identifier") != null) {
                String firstname = req.queryParams("firstname");
                String lastname = req.queryParams("lastname");
                String username = req.queryParams("identifier");
                String password = req.queryParams("password");

                UserCore.create(firstname, lastname, username, password);

                UserEntity userEntity = new UserEntity();
                userEntity.setIdentifier(username);
                userEntity.setPassword(password);

                try {
                    res.cookie("/", "auth", doLogin.login(userEntity), 36000, false, true);
                    authentificateUser(userEntity);
                } catch (Exception e) {
                    res.redirect("/login", 301);
                    System.err.println(e.getMessage());
                }
            }
            res.redirect("/", 301);
            return "";
        });

        get("/signout", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            res.removeCookie("auth");
            UserCore.delete(connectedUser.getId());
            res.redirect("/", 301);
            return "";
        });

        // Utilisateurs

        get("/users", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            return UserGUI.getAllUsers(connectedUser);
        });

        get("/users/:id-user", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);

            int userId = parseIdFromRequest(req, "id-user");
            if (userId == -1) {
                res.status(400);
                return null;
            }

            return UserGUI.getUser(connectedUser, userId);
        });

        // Pokémons

        get("/pokemons", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            return PokemonGUI.getAllPokemons(connectedUser);
        });

        get("/users/:id-user/:id-pkmn", (req, res) -> {
            int userId = parseIdFromRequest(req, "id-user");
            int pokemonId = parseIdFromRequest(req, "id-pkmn");
            if (userId == -1 || pokemonId == -1) {
                res.status(400);
                return null;
            }

            res.redirect("/pokemons/" + pokemonId, 301);
            return null;
        });

        get("/pokemons/:id-pkmn", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);

            int pokemonId = parseIdFromRequest(req, "id-pkmn");
            if (pokemonId == -1) {
                res.status(400);
                return null;
            }

            return PokemonGUI.getPokemon(connectedUser, pokemonId);
        });

        post("/pokemons/:id-pkmn/levelup", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int pokemonId = parseIdFromRequest(req, "id-pkmn");
            if (pokemonId == -1) {
                res.status(400);
                return null;
            }
            PokemonEntity pokemon = PokemonCore.getById(pokemonId);

            if (connectedUser.getNbUpgrades() > 0 && pokemon.getLevel() < 100) {
                UserCore.useOneUpgrade(connectedUser);
                PokemonCore.update(pokemonId, pokemon.getSpecies(), pokemon.getNickname(), pokemon.getLevel() + 1, pokemon.getOwner());
            }

            res.redirect("/pokemons/" + pokemonId);
            return null;
        });

        post("/pokemons/:id-pkmn/rename", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int pokemonId = parseIdFromRequest(req, "id-pkmn");
            if (pokemonId == -1) {
                res.status(400);
                return null;
            }

            PokemonEntity pokemon = PokemonCore.getById(pokemonId);

            if (connectedUser.getId() == pokemon.getOwner().getId()) {
                PokemonCore.update(pokemonId, pokemon.getSpecies(), req.queryParams("nickname"), pokemon.getLevel(), pokemon.getOwner());
            }

            res.redirect("/pokemons/" + pokemonId);
            return null;
        });

        post("/pokemons/:id-pkmn/delete", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int pokemonId = parseIdFromRequest(req, "id-pkmn");
            if (pokemonId == -1) {
                res.status(400);
                return null;
            }

            PokemonEntity pokemon = PokemonCore.getById(pokemonId);
            if (connectedUser.getId() == pokemon.getOwner().getId()) {
                PokemonCore.delete(pokemonId);
            }

            res.redirect("/users/" + connectedUser.getId());
            return null;
        });

        // Échanges

        get("/exchanges", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            return ExchangeGUI.getAllExchanges(connectedUser);
        });

        get("/exchanges/exp/:id-user", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);

            int userId = parseIdFromRequest(req, "id-user");
            if (userId == -1) {
                res.status(400);
                return null;
            }

            return ExchangeGUI.getAllByExp(connectedUser, userId);
        });

        get("/exchanges/dest/:id-user", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);

            int userId = parseIdFromRequest(req, "id-user");
            if (userId == -1) {
                res.status(400);
                return null;
            }

            return ExchangeGUI.getAllByDest(connectedUser, userId);
        });

        get("/exchanges/:id-exchange", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);

            int exchangeId = parseIdFromRequest(req, "id-exchange");
            if (exchangeId == -1) {
                res.status(400);
                return null;
            }

            return ExchangeGUI.getExchange(connectedUser, exchangeId);
        });

        post("/exchanges/start", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int idPokemonExp = Integer.parseInt(req.queryParams("exp"));
            if (idPokemonExp == -1) {
                res.status(400);
                return null;
            }
            PokemonEntity exp = PokemonCore.getById(idPokemonExp);

            int idPokemonDest = Integer.parseInt(req.queryParams("dest"));
            if (idPokemonDest == -1) {
                res.status(400);
                return null;
            }
            PokemonEntity dest = PokemonCore.getById(idPokemonDest);

            if (connectedUser.getId() == exp.getOwner().getId()) {
                ExchangeEntity exch = ExchangeCore.create(exp, dest, "PROPOSED");
                res.redirect("/exchanges/" + exch.getId());
            } else {
                res.redirect("/exchanges");
            }

            return null;
        });

        post("/exchanges/:id-exchange/cancel", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int exchangeId = parseIdFromRequest(req, "id-exchange");
            if (exchangeId == -1) {
                res.status(400);
                return null;
            }

            ExchangeEntity exchange = ExchangeCore.getById(exchangeId);

            if (connectedUser.getId() == exchange.getPokemonExp().getOwner().getId()) {
                ExchangeCore.update(exchangeId, exchange.getPokemonExp(), exchange.getPokemonDest(), "CANCELLED");
            }

            res.redirect("/exchanges");
            return null;
        });

        post("/exchanges/:id-exchange/agree", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int exchangeId = parseIdFromRequest(req, "id-exchange");
            if (exchangeId == -1) {
                res.status(400);
                return null;
            }

            ExchangeEntity exchange = ExchangeCore.getById(exchangeId);

            if (connectedUser.getId() == exchange.getPokemonDest().getOwner().getId()) {
                PokemonEntity exp = exchange.getPokemonExp();
                PokemonEntity dest = exchange.getPokemonDest();

                UserEntity userExp = exp.getOwner();
                UserEntity userDest = dest.getOwner();

                PokemonCore.update(exp.getId(), exp.getSpecies(), exp.getNickname(), exp.getLevel(), userDest);
                PokemonCore.update(dest.getId(), dest.getSpecies(), dest.getNickname(), dest.getLevel(), userExp);

                ExchangeCore.update(exchangeId, exchange.getPokemonExp(), exchange.getPokemonDest(), "AGREED");
            }

            res.redirect("/exchanges");
            return null;
        });

        post("/exchanges/:id-exchange/disagree", (req, res) -> {
            UserEntity connectedUser = getAuthenticatedUser(req);
            if (connectedUser == null) {
                res.redirect("/login", 301);
                return null;
            }

            int exchangeId = parseIdFromRequest(req, "id-exchange");
            if (exchangeId == -1) {
                res.status(400);
                return null;
            }

            ExchangeEntity exchange = ExchangeCore.getById(exchangeId);

            if (connectedUser.getId() == exchange.getPokemonDest().getOwner().getId()) {
                ExchangeCore.update(exchangeId, exchange.getPokemonExp(), exchange.getPokemonDest(), "DISAGREED");
            }

            res.redirect("/exchanges");
            return null;
        });
    }

    private static int parseIdFromRequest(spark.Request req, String idName) {
        String id = req.params(":" + idName);
        // Si l'id est null ou pas un entier, alors on retourne une erreur
        if (id == null) {
            return -1;
        }

        int myId;

        try {
            myId = Integer.parseInt(id);
        } catch (Exception e) {
            return -1;
        }

        return myId;
    }

    private static UserEntity getAuthenticatedUser(Request req) throws ServiceException {
        String token = req.cookie("auth");

        if (token == null) {
            return null;
        } else {
            UserEntity infoUser = doLogin.introspect(token);
            return UserCore.getById(infoUser.getId());
        }

        // return token == null ? null : doLogin.introspect(token);
    }

    private static void authentificateUser(UserEntity user) throws ServiceException {
        UserEntity connectedUser = UserCore.getByIdentifier(user.getIdentifier(), false);

        // Changements journaliers
        if (!connectedUser.getLastConnection().isEqual(LocalDate.now())) {
            // Apparition du nouveau Pokémon
            int idAPIRandomPokemon = new Random().nextInt(NB_POKEMONS) + 1; // Nombre aléatoire entre 1 et NB_POKEMONS
            PokedexEntity species = PokemonCore.getPokedexById(idAPIRandomPokemon);
            System.err.println(species.getName());
            PokemonCore.create(species, species.getName(), 1, connectedUser);

            // Réinitialisation du nombre d'upgrades possibles
            UserCore.resetNbUpgrades(connectedUser);
        }

        // Mise à jour de la date
        UserCore.updateLastConnection(connectedUser);
    }
}