package com.alttd.altitudeweb.services.site;

import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.luckperms.Player;
import com.alttd.altitudeweb.database.luckperms.PlayerWithGroup;
import com.alttd.altitudeweb.database.luckperms.TeamMemberMapper;
import com.alttd.altitudeweb.database.proxyplaytime.StaffPlaytimeMapper;
import com.alttd.altitudeweb.database.proxyplaytime.StaffPt;
import com.alttd.altitudeweb.mappers.StaffPtToStaffPlaytimeMapper;
import com.alttd.altitudeweb.model.StaffPlaytimeDto;
import com.alttd.altitudeweb.setup.Connection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class StaffPtService {
    private final static HashMap<String, String> STAFF_GROUPS_MAP = new HashMap<>();
    private final static String STAFF_GROUPS;
    static {
        STAFF_GROUPS_MAP.put("group.owner", "Owner");
        STAFF_GROUPS_MAP.put("group.manager", "Manager");
        STAFF_GROUPS_MAP.put("group.admin", "Admin");
        STAFF_GROUPS_MAP.put("group.headmod", "Head Mod");
        STAFF_GROUPS_MAP.put("group.moderator", "Moderator");
        STAFF_GROUPS_MAP.put("group.trainee", "Trainee");
        STAFF_GROUPS_MAP.put("group.developer", "Developer");
        STAFF_GROUPS = STAFF_GROUPS_MAP.keySet().stream()
                .map(group -> "'" + group + "'")
                .collect(Collectors.joining(", "));
    }
    private final StaffPtToStaffPlaytimeMapper staffPtToStaffPlaytimeMapper;

    public Optional<List<StaffPlaytimeDto>> getStaffPlaytime(Instant from, Instant to) {
        CompletableFuture<List<PlayerWithGroup>> staffMembersFuture = new CompletableFuture<>();
        CompletableFuture<List<StaffPt>> staffPlaytimeFuture = new CompletableFuture<>();
        Connection.getConnection(Databases.LUCK_PERMS)
                .runQuery(sqlSession -> {
                    log.debug("Loading staff members");
                    try {
                        List<PlayerWithGroup> staffMemberList = sqlSession.getMapper(TeamMemberMapper.class)
                                .getTeamMembersOfGroupList(STAFF_GROUPS);
                        staffMembersFuture.complete(staffMemberList);
                    } catch (Exception e) {
                        log.error("Failed to load staff members", e);
                        staffMembersFuture.completeExceptionally(e);
                    }
                });
        List<PlayerWithGroup> staffMembers = staffMembersFuture.join().stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(PlayerWithGroup::uuid, player -> player, (player1, player2) -> player1),
                        m -> new ArrayList<>(m.values())));
        Connection.getConnection(Databases.PROXY_PLAYTIME)
                .runQuery(sqlSession -> {
                    String staffUUIDs = staffMembers.stream()
                            .map(PlayerWithGroup::uuid)
                            .map(uuid -> "'" + uuid + "'")
                            .collect(Collectors.joining(","));
                    log.debug("Loading staff playtime for group");
                    try {
                        List<StaffPt> sessionsDuring = sqlSession.getMapper(StaffPlaytimeMapper.class)
                                .getSessionsDuring(from.toEpochMilli(), to.toEpochMilli(), staffUUIDs);
                        staffPlaytimeFuture.complete(sessionsDuring);
                    } catch (Exception e) {
                        log.error("Failed to load staff playtime", e);
                        staffPlaytimeFuture.completeExceptionally(e);
                    }
                });
        List<StaffPt> join = staffPlaytimeFuture.join();
        HashMap<String, String> staffGroupsMap = new HashMap<>(STAFF_GROUPS_MAP);
        return Optional.of(staffPtToStaffPlaytimeMapper.map(join, staffMembers, from.toEpochMilli(), to.toEpochMilli(), staffGroupsMap));
    }
}
