package pp.monopoly.model;

import pp.monopoly.game.server.Player;
import pp.monopoly.model.fields.PropertyField;

import java.util.List;

/**
 * Helper class that handles the trade logic between two players.
 * Manages trade initiation, validation, acceptance, and rejection involving multiple properties, money, and jail cards.
 */
public class TradeHandler {

    /**
     * Initiates a trade offer between two players involving properties, money, and jail cards.
     *
     * @param sender             the Player who is initiating the trade
     * @param receiver           the Player who is the target of the trade offer
     * @param offeredAmount      the amount of money the sender offers
     * @param offeredProperties  the list of properties the sender offers
     * @param offeredJailCards   the number of jail cards the sender offers
     * @param requestedAmount    the amount of money the sender requests from the receiver
     * @param requestedProperties the list of properties the sender requests from the receiver
     * @param requestedJailCards the number of jail cards the sender requests from the receiver
     * @return true if the trade offer is valid and initiated, false otherwise
     */
    public boolean initiateTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties,
                                 int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) {
        // Validate the trade offer
        if (!validateTrade(sender, offeredAmount, offeredProperties, offeredJailCards, receiver, requestedAmount, requestedProperties, requestedJailCards)) {
            System.out.println("Trade offer is invalid.");
            return false;
        }

        // Notify the receiver about the trade offer (this would be an actual message in a real implementation)
        System.out.println("Trade offer initiated by " + sender.getName() + " to " + receiver.getName());
        return true;
    }

    /**
     * Accepts the trade offer and completes the trade between two players.
     *
     * @param sender             the Player who initiated the trade
     * @param receiver           the Player who accepted the trade
     * @param offeredAmount      the amount of money to transfer from the sender to the receiver
     * @param offeredProperties  the list of properties to transfer from the sender to the receiver
     * @param offeredJailCards   the number of jail cards to transfer from the sender to the receiver
     * @param requestedAmount    the amount of money to transfer from the receiver to the sender
     * @param requestedProperties the list of properties to transfer from the receiver to the sender
     * @param requestedJailCards the number of jail cards to transfer from the receiver to the sender
     */
    public void acceptTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties,
                            int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) {
        // Transfer money
        sender.earnMoney(-offeredAmount);  // Deduct money from the sender
        receiver.earnMoney(offeredAmount); // Add money to the receiver

        receiver.earnMoney(-requestedAmount); // Deduct money from the receiver
        sender.earnMoney(requestedAmount);    // Add money to the sender

        // Transfer ownership of the properties from sender to receiver
        if (offeredProperties != null) {
            for (PropertyField property : offeredProperties) {
                transferProperty(sender, receiver, property);
            }
        }

        // Transfer ownership of the properties from receiver to sender
        if (requestedProperties != null) {
            for (PropertyField property : requestedProperties) {
                transferProperty(receiver, sender, property);
            }
        }

        // Transfer jail cards
        transferJailCards(sender, receiver, offeredJailCards);
        transferJailCards(receiver, sender, requestedJailCards);

        System.out.println("Trade accepted. " + sender.getName() + " and " + receiver.getName() + " completed the trade.");
    }

    /**
     * Rejects the trade offer.
     *
     * @param receiver the Player who is rejecting the trade
     */
    public void rejectTrade(Player receiver) {
        System.out.println("Trade rejected by " + receiver.getName());
    }

    /**
     * Validates a trade offer by checking if the sender and receiver own the properties involved,
     * have sufficient funds for the money involved in the trade, and have enough jail cards.
     *
     * @param sender             the Player initiating the trade
     * @param offeredAmount      the amount of money the sender is offering
     * @param offeredProperties  the list of properties the sender is offering
     * @param offeredJailCards   the number of jail cards the sender is offering
     * @param receiver           the Player receiving the trade offer
     * @param requestedAmount    the amount of money the sender is requesting
     * @param requestedProperties the list of properties the sender is requesting from the receiver
     * @param requestedJailCards the number of jail cards the sender is requesting from the receiver
     * @return true if the trade offer is valid, false otherwise
     */
    private boolean validateTrade(Player sender, int offeredAmount, List<PropertyField> offeredProperties, int offeredJailCards,
                                  Player receiver, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) {
        // Check if sender has enough money to offer
        if (sender.getAccountBalance() < offeredAmount) {
            System.out.println("Sender does not have enough balance to make this offer.");
            return false;
        }

        // Check if receiver has enough money to offer
        if (receiver.getAccountBalance() < requestedAmount) {
            System.out.println("Receiver does not have enough balance to fulfill requested amount.");
            return false;
        }

        // Check if sender owns all the offered properties
        if (offeredProperties != null) {
            for (PropertyField property : offeredProperties) {
                if (!sender.getProperties().contains(property)) {
                    System.out.println("Sender does not own the property " + property.getName() + " being offered.");
                    return false;
                }
            }
        }

        // Check if receiver owns all the requested properties
        if (requestedProperties != null) {
            for (PropertyField property : requestedProperties) {
                if (!receiver.getProperties().contains(property)) {
                    System.out.println("Receiver does not own the property " + property.getName() + " requested.");
                    return false;
                }
            }
        }

        // Check if sender has enough jail cards to offer
        if (sender.getNumJailCard() < offeredJailCards) {
            System.out.println("Sender does not have enough jail cards to offer.");
            return false;
        }

        // Check if receiver has enough jail cards to fulfill the request
        if (receiver.getNumJailCard() < requestedJailCards) {
            System.out.println("Receiver does not have enough jail cards to fulfill the request.");
            return false;
        }

        return true;
    }

    /**
     * Transfers a property from one player to another.
     *
     * @param from     the Player transferring the property
     * @param to       the Player receiving the property
     * @param property the PropertyField being transferred
     */
    private void transferProperty(Player from, Player to, PropertyField property) {
        from.sellProperty(property);
        to.buyProperty(property);
        property.setOwner(to); // Update the property's owner

        System.out.println("Property " + property.getName() + " transferred from " + from.getName() + " to " + to.getName());
    }

    /**
     * Transfers jail cards between players.
     *
     * @param from the Player transferring jail cards
     * @param to   the Player receiving jail cards
     * @param numCards the number of jail cards to transfer
     */
    private void transferJailCards(Player from, Player to, int numCards) {
        for (int i = 0; i < numCards; i++) {
            from.removeJailCard();
            to.addJailCard();
        }
        System.out.println("Transferred " + numCards + " jail card(s) from " + from.getName() + " to " + to.getName());
    }
}
