/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.bausystem.features.region;

import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.util.SelectCommand;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionUtils;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.bausystem.utils.WorldEditUtils;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.sql.SchematicNode;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;

import java.util.logging.Level;

@Linked
public class RegionCommand extends SWCommand {

    @LinkedInstance
    public SelectCommand selectCommand;

    @LinkedInstance
    public ColorCommand colorCommand;

    public RegionCommand() {
        super("region", "rg");
    }

    static boolean checkGlobalRegion(Region region, Player p) {
        if (region.getType().isGlobal()) {
            BauSystem.MESSAGE.send("REGION_REGION_NO_REGION", p);
            return true;
        }
        return false;
    }

    @Register(value = "undo", description = "REGION_REGION_HELP_UNDO")
    public void undoCommand(@Validator Player p) {
        Region region = Region.getRegion(p.getLocation());
        if (region.getHistory() == RegionHistory.EMPTY) {
            BauSystem.MESSAGE.send("REGION_REGION_NO_REGION", p);
            return;
        }

        if (region.getHistory().undo()) {
            RegionUtils.message(region, "REGION_REGION_UNDID");
        } else {
            BauSystem.MESSAGE.send("REGION_REGION_NOTHING_UNDO", p);
        }
    }

    @Register(value = "redo", description = "REGION_REGION_HELP_REDO")
    public void redoCommand(@Validator Player p) {
        Region region = Region.getRegion(p.getLocation());
        if (region.getHistory() == RegionHistory.EMPTY) {
            BauSystem.MESSAGE.send("REGION_REGION_NO_REGION", p);
            return;
        }

        if (region.getHistory().redo()) {
            RegionUtils.message(region, "REGION_REGION_REDID");
        } else {
            BauSystem.MESSAGE.send("REGION_REGION_NOTHING_REDO", p);
        }
    }

    @Register(value = "restore", description = "REGION_REGION_HELP_RESTORE")
    public void genericRestoreCommand(@Validator Player p) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) return;

        try {
            PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.FileProvider(region.getArea().getResetFile()))
                    .ignoreAir(true)
                    .color(region.getRegionData().get(Flag.COLOR).getWithDefault());
            region.getArea().reset(pasteBuilder, false);
            RegionUtils.message(region, "REGION_REGION_RESTORED");
        } catch (SecurityException e) {
            BauSystem.MESSAGE.send("REGION_REGION_FAILED_RESTORE", p);
            Bukkit.getLogger().log(Level.WARNING, "Failed restore", e);
        }
    }

    @Register(value = "restore", description = "REGION_REGION_HELP_RESTORE_SCHEMATIC")
    public void schematicRestoreCommand(@Validator Player p, SchematicNode node) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) return;

        if (node.isDir()) {
            BauSystem.MESSAGE.send("ONLY_SCHEMS", p);
            return;
        }

        try {
            PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.SchematicProvider(node))
                    .ignoreAir(true)
                    .color(region.getRegionData().get(Flag.COLOR).getWithDefault());
            region.getArea().reset(pasteBuilder, false);
            RegionUtils.message(region, "REGION_REGION_RESTORED");
        } catch (SecurityException e) {
            BauSystem.MESSAGE.send("REGION_REGION_FAILED_RESTORE", p);
            Bukkit.getLogger().log(Level.WARNING, "Failed restore", e);
        }
    }

    @Register(value = "copypoint", description = "REGION_REGION_HELP_COPYPOINT")
    public void copyPointCommand(Player p) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) {
            return;
        }
        if (region.getBuildArea().isEmpty()) {
            BauSystem.MESSAGE.send("REGION_REGION_TP_UNKNOWN", p);
            return;
        }
        p.teleport(region.getBuildArea().getCopyPoint().toLocation(p, 0.5, 0, 0.5), PlayerTeleportEvent.TeleportCause.COMMAND);
        BauSystem.MESSAGE.send("REGION_REGION_TP_COPY", p);
    }

    @Register(value = "testblockpoint", description = "REGION_REGION_HELP_TESTBLOCKPOINT")
    @Register("tbpoint")
    public void testBlockPointCommand(Player p) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) {
            return;
        }
        if (region.getTestblockArea().isEmpty()) {
            BauSystem.MESSAGE.send("REGION_REGION_TP_UNKNOWN", p);
            return;
        }
        p.teleport(region.getTestblockArea().getCopyPoint().toLocation(p, 0.5, 0, 0.5), PlayerTeleportEvent.TeleportCause.COMMAND);
        BauSystem.MESSAGE.send("REGION_REGION_TP_TEST_BLOCK", p);
    }

    @Register(value = "copy", description = "REGION_REGION_HELP_COPY")
    public void copyCommand(@Validator Player p, @OptionalValue("") @StaticValue(value = {"", "-e", "-s"}, allowISE = true) int option) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) {
            return;
        }
        if (region.getBuildArea().isEmpty()) {
            BauSystem.MESSAGE.send("REGION_REGION_NO_BUILD", p);
            return;
        }

        Point minPoint = region.getBuildArea().getMinPoint(false);
        Point maxPoint = region.getBuildArea().getMaxPoint(false);
        switch (option) {
            case 0:
                break;
            case 1:
                minPoint = region.getBuildArea().getMinPoint(true);
                maxPoint = region.getBuildArea().getMaxPoint(true);
                break;
            case 2:
                Pair<Location, Location> selection = WorldEditUtils.getSelection(p);
                minPoint = Point.fromLocation(selection.getKey());
                maxPoint = Point.fromLocation(selection.getValue());
                break;
            default:
                break;
        }

        Clipboard clipboard = FlatteningWrapper.impl.copy(minPoint, maxPoint, region.getBuildArea().getCopyPoint());
        WorldEdit.getInstance()
                .getSessionManager()
                .get(BukkitAdapter.adapt(p))
                .setClipboard(new ClipboardHolder(clipboard));
        BauSystem.MESSAGE.send("REGION_REGION_COPY_DONE", p);
    }

    @Register(value = "paste", description = "REGION_REGION_HELP_PASTE")
    public void pasteCommand(@Validator Player p, @OptionalValue("") @StaticValue(value = {"", "-a", "-s", "-as", "-sa"}, allowISE = true) int options) {
        Region region = Region.getRegion(p.getLocation());
        if (checkGlobalRegion(region, p)) {
            return;
        }
        if (region.getBuildArea().isEmpty()) {
            BauSystem.MESSAGE.send("REGION_REGION_NO_BUILD", p);
            return;
        }

        ClipboardHolder clipboardHolder = WorldEdit.getInstance()
                .getSessionManager()
                .get(BukkitAdapter.adapt(p))
                .getClipboard();

        boolean selectPasted = false;
        boolean ignoreAir = false;
        switch (options) {
            case 0:
                break;
            case 1:
                ignoreAir = true;
                break;
            case 2:
                selectPasted = true;
                break;
            case 3:
            case 4:
                selectPasted = true;
                ignoreAir = true;
                break;
            default:
                break;
        }

        try (EditSession e = WorldEditUtils.getEditSession(p)) {
            Operations.completeBlindly(clipboardHolder.createPaste(e).ignoreAirBlocks(ignoreAir).to(toBlockVector3(region.getBuildArea().getCopyPoint())).build());
            WorldEditUtils.addToPlayer(p, e);

            if (selectPasted) {
                Clipboard clipboard = clipboardHolder.getClipboards().get(0);
                BlockVector3 minPointSelection = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()).add(toBlockVector3(region.getBuildArea().getCopyPoint()));
                BlockVector3 maxPointSelection = clipboard.getRegion().getMaximumPoint().subtract(clipboard.getOrigin()).add(toBlockVector3(region.getBuildArea().getCopyPoint()));
                FlatteningWrapper.impl.setSelection(p, Point.fromBlockVector3(minPointSelection), Point.fromBlockVector3(maxPointSelection));
            }
            BauSystem.MESSAGE.send("REGION_REGION_PASTE_DONE", p);
        }
    }

    private BlockVector3 toBlockVector3(Point point) {
        return BlockVector3.at(point.getX(), point.getY(), point.getZ());
    }
}
