/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2024  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.velocitycore.commands;

import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.UserPerm;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

public class BuilderCloudCommand extends SWCommand {

    public BuilderCloudCommand() {
        super("buildercloud", UserPerm.BUILD, "builder");
    }

    @Register(value = "create", description = "BUILDERCLOUD_CREATE_USAGE")
    public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @OptionalValue("") @Mapper("generator") @AllowNull File generator) {
        mapFile(version, map).mkdir();
        sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start());
    }

    @Register(description = "BUILDERCLOUD_USAGE")
    public void start(PlayerChatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
        if(!mapFile(version, map).exists()) {
            sender.system("BUILDERCLOUD_UNKNOWN_MAP");
            return;
        }

        new ServerStarter().builder(version, map, null).send(sender.getPlayer()).start();
    }

    @Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE")
    public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) {
        File oldMap = mapFile(version, oldName);
        if(!oldMap.exists()) {
            sender.system("BUILDERCLOUD_UNKNOWN_MAP");
            return;
        }

        File newMap = mapFile(version, newName);
        if(newMap.exists()) {
            sender.system("BUILDERCLOUD_EXISTING_MAP");
            return;
        }

        try {
            Files.move(oldMap.toPath(), newMap.toPath());
        } catch (IOException e) {
            throw new SecurityException(e);
        }

        sender.system("BUILDERCLOUD_RENAMED");
    }

    @Register(value = "deploy", description = "BUILDERCLOUD_DEPLOY_USAGE")
    public void deploy(Chatter sender, @Mapper("nonHistoricArenaMode") ArenaMode arenaMode, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
        if(!mapFile(version, map).exists()) {
            sender.system("BUILDERCLOUD_UNKNOWN_MAP");
            return;
        }

        VelocityCore.schedule(() -> {
            VelocityCore.local.execute("deployarena.py", arenaMode.getConfig(), Integer.toString(version.getVersionSuffix()), map);
            ArenaMode.init();
            sender.system("BUILDERCLOUD_DEPLOY_FINISHED");
        }).schedule();
    }

    @Cached(global = true)
    @Mapper(value = "map", local = true)
    private TypeMapper<String> mapTypeMapper() {

        return new TypeMapper<String>() {
            @Override
            public String map(Chatter sender, PreviousArguments previousArguments, String s) {
                return s;
            }

            @Override
            public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
                File folder = getWorldFolder(previousArguments, 1);

                String[] files;
                if(folder == null || (files = folder.list()) == null)
                    return Collections.emptyList();

                return Arrays.stream(files).filter(file -> new File(folder, file).isDirectory()).filter(file -> s.startsWith(".") || !file.startsWith(".")).toList();
            }
        };
    }

    @Cached(global = true)
    @Mapper(value = "generator", local = true)
    private TypeMapper<File> generatorTypeMapper() {

        return new TypeMapper<File>() {
            @Override
            public File map(Chatter sender, PreviousArguments previousArguments, String s) {
                if(s.isEmpty())
                    return null;

                File folder = getWorldFolder(previousArguments, 2);

                if(folder == null)
                    throw new SecurityException();

                File generator = new File(folder, s + ".dat");
                if(!generator.exists() || !generator.isFile())
                    throw new SecurityException();

                return generator;
            }

            @Override
            public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
                File folder = getWorldFolder(previousArguments, 2);

                String[] files;
                if(folder == null || (files = folder.list()) == null)
                    return Collections.emptyList();

                return Arrays.stream(files).filter(file -> new File(folder, file).isFile()).filter(file -> file.endsWith(".dat")).map(file -> file.substring(0, file.length() - 4)).toList();
            }
        };
    }

    private File mapFile(ServerVersion version, String map) {
        return new File(version.getWorldFolder(ServerStarter.BUILDER_BASE_PATH), map);
    }

    private File getWorldFolder(PreviousArguments previousArguments, int offset) {
        ServerVersion v = ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
        if(v == null)
            return null;
        return new File(v.getWorldFolder(ServerStarter.BUILDER_BASE_PATH));
    }
}
