/*
 * This file is part of EconomyLite, licensed under the MIT License (MIT). See the LICENSE file at the root of this project for more information.
 */
package io.github.flibio.economylite.modules.loan;

import org.spongepowered.api.text.serializer.TextSerializers;
import org.spongepowered.api.util.Tristate;

import org.spongepowered.api.service.permission.SubjectData;
import io.github.flibio.economylite.modules.loan.event.LoanBalanceChangeEvent;
import io.github.flibio.economylite.EconomyLite;
import io.github.flibio.utils.message.MessageStorage;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.economy.EconomyTransactionEvent;
import org.spongepowered.api.service.economy.Currency;
import org.spongepowered.api.service.economy.transaction.ResultType;
import org.spongepowered.api.text.Text;

import java.math.BigDecimal;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;

public class LoanListener {

    private MessageStorage messages = EconomyLite.getMessageStorage();
    private LoanManager loans;
    private LoanModule module;
    private double maxLoanBal;
    private double intRate;

    public LoanListener(LoanModule module) {
        this.module = module;
        this.loans = module.getLoanManager();
        this.maxLoanBal = module.getMaxLoan();
        this.intRate = module.getInterestRate();
    }

    @Listener
    public void onBalanceChange(EconomyTransactionEvent event) {
        // Check if the transaction failed due to insufficient funds
        if (event.getTransactionResult().getResult().equals(ResultType.ACCOUNT_NO_FUNDS)) {
            // Check if the UUID is in the event
            Optional<UUID> uOpt = event.getCause().first(UUID.class);
            Optional<String> sOpt = event.getCause().first(String.class);
            if (sOpt.isPresent()) {
                if (sOpt.get().equalsIgnoreCase("economylite:loan")) {
                    return;
                }
            }
            if (uOpt.isPresent()) {
                UUID uuid = uOpt.get();
                // Try to get the player
                Optional<Player> pOpt = Sponge.getServer().getPlayer(uuid);
                if (pOpt.isPresent()) {
                    Player player = pOpt.get();
                    // Get loan balance of the player
                    Optional<Double> dOpt = loans.getLoanBalance(uuid);
                    if (dOpt.isPresent()) {
                        double loanBalance = dOpt.get();
                        // Calculate amount of money the player is short of
                        Currency cur = event.getTransactionResult().getCurrency();
                        BigDecimal bal = event.getTransactionResult().getAccount().getBalance(cur);
                        BigDecimal mis = event.getTransactionResult().getAmount().subtract(bal);
                        double misDouble = mis.doubleValue();
                        // Notify player of interest rate
                        player.sendMessage(messages.getMessage("module.loan.interest", "rate", Double.toString(intRate)));
                        // Check how much loan they can take out
                        double maxLoan = (maxLoanBal - loanBalance) / intRate;
                        if (maxLoan <= 0) {
                            return;
                        }
                        if (maxLoan < misDouble) {
                            // Offer the player a smaller loan
                            player.sendMessage(messages.getMessage("module.loan.partial"));
                            player.sendMessage(messages.getMessage("module.loan.ask", "amount",
                                    String.format(Locale.ENGLISH, "%,.2f", maxLoan), "label", getPrefix(maxLoan, cur)));
                            double total = maxLoan * intRate;
                            player.sendMessage(messages.getMessage("module.loan.payment", "amount",
                                    String.format(Locale.ENGLISH, "%,.2f", total), "label", getPrefix(total, cur)));
                            module.tableLoans.remove(uuid);
                            module.tableLoans.put(uuid, maxLoan);
                        } else {
                            // Ask the player if they want a full loan
                            player.sendMessage(messages.getMessage("module.loan.ask", "amount", String.format(Locale.ENGLISH, "%,.2f", mis),
                                    "label", getPrefix(mis.doubleValue(), cur)));
                            BigDecimal total = mis.multiply(BigDecimal.valueOf(intRate));
                            player.sendMessage(messages.getMessage("module.loan.payment", "amount",
                                    String.format(Locale.ENGLISH, "%,.2f", total), "label", getPrefix(total.doubleValue(), cur)));
                            player.sendMessage(LoanTextUtils.yesOrNo("/loan accept", "/loan deny"));
                            module.tableLoans.remove(uuid);
                            module.tableLoans.put(uuid, mis.doubleValue());
                        }
                    }
                }
            }
        }
    }

    @Listener
    public void onLoanChange(LoanBalanceChangeEvent event) {
        UUID uuid = event.getUser();
        for (Player player : Sponge.getServer().getOnlinePlayers()) {
            if (player.getUniqueId().equals(uuid)) {
                if (event.getNewBalance() == 0) {
                    // Remove the debtor permissions
                    module.getPermissions().forEach((perm, val) -> {
                        player.getSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, perm, Tristate.fromBoolean(!val));
                    });
                } else {
                    // Add the debtor permissions
                    module.getPermissions().forEach((perm, val) -> {
                        player.getSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, perm, Tristate.fromBoolean(val));
                    });
                }
            }
        }
    }

    private String getPrefix(double amnt, Currency cur) {
        Text label = cur.getPluralDisplayName();
        if (amnt == 1.0) {
            label = cur.getDisplayName();
        }
        return TextSerializers.FORMATTING_CODE.serialize(label);
    }
}