package com.bitnix.dirtcryptostore.manager;

import com.bitnix.dirtcryptostore.DirtCryptoStorePlugin;
import com.bitnix.dirtcryptostore.model.BlockchainPaymentMatch;
import com.bitnix.dirtcryptostore.model.PendingInvoice;
import com.bitnix.dirtcryptostore.model.Product;
import com.bitnix.dirtcryptostore.model.WalletConfig;
import com.bitnix.dirtcryptostore.util.EffectUtil;
import com.bitnix.dirtcryptostore.util.MessageUtil;
import com.bitnix.dirtcryptostore.util.TitleUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

public class PaymentWatcher implements Runnable {

    private final DirtCryptoStorePlugin plugin;
    private final BlockchainApi blockchainApi;

    public PaymentWatcher(DirtCryptoStorePlugin plugin) {
        this.plugin = plugin;
        this.blockchainApi = new BlockchainApi(plugin);
    }

    @Override
    public void run() {
        plugin.getInvoiceManager().cleanupExpiredInvoices();

        for (PendingInvoice invoice : plugin.getInvoiceManager().getAllInvoices()) {
            if (!invoice.isMatchable()) {
                continue;
            }

            if (invoice.getStatus() == PendingInvoice.Status.PENDING && invoice.isExpired()) {
                plugin.getInvoiceManager().expireInvoice(invoice, "Expired by watcher.");
                notifyExpired(invoice);
                continue;
            }

            BlockchainPaymentMatch match = blockchainApi.findPayment(invoice);
            if (!match.isFound()) {
                continue;
            }

            if (plugin.getInvoiceManager().isTxidAlreadyClaimed(match.getTxid(), invoice.getInvoiceId())) {
                plugin.getInvoiceManager().markUnderReview(
                        invoice,
                        match.getTxid(),
                        match.getConfirmations(),
                        "TXID already claimed by another invoice."
                );
                notifyUnderReview(invoice);
                continue;
            }

            WalletConfig wallet = plugin.getConfigManager().getWallet(invoice.getWalletId());
            int requiredConfirmations = wallet == null
                    ? plugin.getConfig().getInt("settings.payment.confirmations-required", 2)
                    : wallet.getMinConfirmations();

            boolean firstDetection = invoice.getStatus() == PendingInvoice.Status.PENDING;

            if (match.getConfirmations() >= requiredConfirmations) {
                fulfill(invoice, match, false);
            } else {
                plugin.getInvoiceManager().markDetected(
                        invoice,
                        match.getTxid(),
                        match.getConfirmations(),
                        "Payment detected, waiting for confirmations."
                );
                if (firstDetection) {
                    notifyDetected(invoice);
                }
            }
        }
    }

    public void forceFulfillFromAdmin(PendingInvoice invoice, BlockchainPaymentMatch match) {
        fulfill(invoice, match, true);
    }

    private void fulfill(PendingInvoice invoice, BlockchainPaymentMatch match, boolean manual) {
        if (invoice.isFulfilled()) {
            return;
        }

        Product product = plugin.getConfigManager().getProduct(invoice.getProductId());
        WalletConfig wallet = plugin.getConfigManager().getWallet(invoice.getWalletId());

        if (product == null) {
            plugin.getInvoiceManager().markUnderReview(
                    invoice,
                    match.getTxid(),
                    match.getConfirmations(),
                    "Product config missing during fulfillment."
            );
            notifyUnderReview(invoice);
            return;
        }

        plugin.getInvoiceManager().markFulfilled(
                invoice,
                match.getTxid(),
                match.getConfirmations(),
                manual ? "Payment fulfilled manually by admin." : "Payment fulfilled automatically."
        );

        Player player = Bukkit.getPlayer(invoice.getPlayerUuid());
        if (player != null) {
            Bukkit.getScheduler().runTask(plugin, () -> {
                MessageUtil.send(plugin, player, "messages.payment-confirmed",
                        "&aYour crypto payment for &f%product% &ahas been confirmed.",
                        "%product%", product.getDisplayName());
                MessageUtil.send(plugin, player, "messages.payment-fulfilled",
                        "&aYour purchase has been delivered.");
                EffectUtil.playEffect(plugin, player, "payment-confirmed");
                TitleUtil.sendTitle(plugin, player, "payment-confirmed");
            });
        }

        boolean dispatchAsConsole = plugin.getConfig().getBoolean("settings.commands.dispatch-as-console", true);

        for (String command : product.getCommands()) {
            String parsed = command
                    .replace("%player%", invoice.getPlayerName())
                    .replace("%uuid%", invoice.getPlayerUuid().toString())
                    .replace("%product%", product.getId())
                    .replace("%wallet%", wallet == null ? invoice.getWalletId() : wallet.getId())
                    .replace("%txid%", match.getTxid())
                    .replace("%confirmations%", String.valueOf(match.getConfirmations()));

            if (dispatchAsConsole) {
                Bukkit.getScheduler().runTask(plugin, () -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), parsed));
            }
        }

        if (plugin.getConfig().getBoolean("settings.debug", false)) {
            plugin.getLogger().info((manual ? "[MANUAL] " : "[AUTO] ") +
                    "Fulfilled invoice " + invoice.getInvoiceId()
                    + " txid=" + match.getTxid()
                    + " confirmations=" + match.getConfirmations());
        }
    }

    private void notifyExpired(PendingInvoice invoice) {
        Player player = Bukkit.getPlayer(invoice.getPlayerUuid());
        if (player == null) {
            return;
        }

        Bukkit.getScheduler().runTask(plugin, () -> {
            MessageUtil.send(plugin, player, "messages.invoice-expired", "&cThis payment request expired.");
            EffectUtil.playEffect(plugin, player, "expired");
            TitleUtil.sendTitle(plugin, player, "cancelled");
        });
    }

    private void notifyDetected(PendingInvoice invoice) {
        Player player = Bukkit.getPlayer(invoice.getPlayerUuid());
        Product product = plugin.getConfigManager().getProduct(invoice.getProductId());
        if (player == null || product == null) {
            return;
        }

        Bukkit.getScheduler().runTask(plugin, () -> {
            MessageUtil.send(plugin, player, "messages.payment-detected",
                    "&aPayment detected for &f%product%&a. Waiting for confirmations...",
                    "%product%", product.getDisplayName());
            EffectUtil.playEffect(plugin, player, "payment-detected");
            TitleUtil.sendTitle(plugin, player, "payment-detected");
        });
    }

    private void notifyUnderReview(PendingInvoice invoice) {
        Player player = Bukkit.getPlayer(invoice.getPlayerUuid());
        if (player == null) {
            return;
        }

        Bukkit.getScheduler().runTask(plugin, () -> {
            MessageUtil.send(plugin, player, "messages.payment-under-review",
                    "&eA matching payment was found but needs review.");
        });
    }
}
