/*
 *   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.schematicsystem.commands.schematiccommand.parts;

import de.steamwar.command.*;
import de.steamwar.linkage.Linked;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommand;
import de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SchematicType;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils.createCachedSchemList;
import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils.getUser;

@AbstractSWCommand.PartOf(SchematicCommand.class)
@Linked
public class SearchPart extends SWCommand {

    private static final Map<String, AbstractTypeMapper<CommandSender, ?>> searchMapper = new HashMap<>();


    static {
        searchMapper.put("-type", SWCommandUtils.createMapper(SchematicType.values().stream().map(SchematicType::name).toArray(String[]::new)));
        searchMapper.put("-owner", SWCommandUtils.createMapper(Function.identity(), (commandSender, s) -> Collections.singletonList(s)));
        Class<?> clazz = Material.class;
        searchMapper.put("-item", SWCommandUtils.createEnumMapper((Class<Enum<?>>) clazz));
        searchMapper.put("-public", null);
        searchMapper.put("-ignoreCase", null);
        searchMapper.put("-exclude", SWCommandUtils.createMapper(Function.identity(), (commandSender, s) -> Collections.singletonList(s)));
        searchMapper.put("-excludeType", SWCommandUtils.createMapper(SchematicType.values().stream().map(SchematicType::name).toArray(String[]::new)));
        searchMapper.put("-excludeOwner", SWCommandUtils.createMapper(Function.identity(), (commandSender, s) -> Collections.singletonList(s)));
    }

    public SearchPart() {
        super(null);
    }

    public boolean containsCheckCase(String s, String s2, AtomicBoolean isIgnoreCase) {
        if (isIgnoreCase.get()) {
            return s.toLowerCase().contains(s2.toLowerCase());
        }
        return s.contains(s2);
    }

    @Register("search")
    public void schemSearch(Player player, @OptionalValue("1") int page, @Mapper("searchMapper") String... query) {
        SteamwarUser user = getUser(player);
        int userId = user.getId();
        List<Predicate<SchematicNode>> predicates = new ArrayList<>();
        List<String> nameList = new ArrayList<>();
        int i = 0;
        AtomicBoolean isIgnoreCase = new AtomicBoolean(false);
        while (i < query.length) {
            String current = query[i];
            if (searchMapper.containsKey(current)) {
                if (searchMapper.get(current) == null) {
                    switch (current) {
                        case "-public":
                            userId = 0;
                            break;
                        case "-ignoreCase":
                            isIgnoreCase.set(true);
                            break;
                        default:
                            throw new IllegalStateException("Unexpected value: " + current);
                    }
                } else if (i + 1 < query.length) {
                    int finalI = i;
                    switch (current) {
                        case "-type":
                            predicates.add(node -> !node.isDir() && node.getSchemtype().name().equalsIgnoreCase(query[finalI + 1]));
                            break;
                        case "-item":
                            predicates.add(node -> node.getItem().equalsIgnoreCase(query[finalI + 1]));
                            break;
                        case "-owner":
                            SteamwarUser steamwarUser = SteamwarUser.get(query[finalI + 1]);
                            if (steamwarUser == null) {
                                SchematicSystem.MESSAGE.send("COMMAND_SEARCH_NOT_A_PLAYER", player, query[finalI + 1]);
                                return;
                            }
                            predicates.add(node -> node.getOwner() == steamwarUser.getId());
                            break;
                        case "-exclude":
                            predicates.add(node -> !node.getName().contains(query[finalI + 1]));
                            break;
                        case "-excludeType":
                            predicates.add(node -> !node.isDir() && !node.getSchemtype().name().equalsIgnoreCase(query[finalI + 1]));
                            break;
                        case "-excludeOwner":
                            SteamwarUser steamwarUser1 = SteamwarUser.get(query[finalI + 1]);
                            if (steamwarUser1 == null) {
                                SchematicSystem.MESSAGE.send("COMMAND_SEARCH_NOT_A_PLAYER", player, query[finalI + 1]);
                                return;
                            }
                            predicates.add(node -> node.getOwner() != steamwarUser1.getId());
                            break;
                        default:
                            throw new IllegalStateException("Unexpected value: " + current);
                    }
                    i++;
                }
            } else {
                predicates.add(node -> containsCheckCase(node.getName(), current, isIgnoreCase));
                nameList.add(current);
            }
            i++;
        }

        List<SchematicNode> nodes = SchematicNode.getAll(SteamwarUser.byId(userId)).stream().filter(node -> {
            for (Predicate<SchematicNode> predicate : predicates) {
                if (!predicate.test(node)) {
                    return false;
                }
            }
            return true;
        }).collect(Collectors.toList());
        createCachedSchemList(player, nodes, Math.max(page - 1, 0), null, SchematicCommandUtils.SchematicListBehavior.builder().setPublics(userId == 0).setShowPath(false).setRenderHook(s -> {
            for (String ss : nameList) {
                s = s.replace(ss, "§e§l" + ss + "§7");
            }
            return "§7" + s;
        }).setNonCachedBreadcrumbs(true).build());
    }

    @Mapper(value = "searchMapper", local = true)
    public TypeMapper<String> searchTypeMapper() {
        return new TypeMapper<String>() {
            @Override
            public String map(CommandSender commandSender, String[] previousArguments, String s) {
                return s;
            }

            @Override
            public Collection<String> tabCompletes(CommandSender commandSender, String[] strings, String s) {
                if (strings.length == 0) {
                    List<String> list = new ArrayList<>();
                    list.add(s);
                    list.addAll(searchMapper.keySet());
                    return list;
                }
                String last = strings[strings.length - 1];
                if (searchMapper.containsKey(last)) {
                    AbstractTypeMapper<CommandSender, ?> mapper = searchMapper.get(last);
                    if (mapper == null) {
                        List<String> list = new ArrayList<>(searchMapper.keySet());
                        list.add(s);
                        return list;
                    }
                    return mapper.tabCompletes(commandSender, strings, s);
                }
                List<String> list = new ArrayList<>(searchMapper.keySet());
                list.add(s);
                return list;
            }
        };
    }
}
