package de.avatic.lcc.repositories.users;

import de.avatic.lcc.model.db.users.Group;
import de.avatic.lcc.model.db.users.User;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Repository
public class UserRepository {

    private final JdbcTemplate jdbcTemplate;
    private final GroupRepository groupRepository;

    public UserRepository(JdbcTemplate jdbcTemplate, GroupRepository groupRepository) {
        this.jdbcTemplate = jdbcTemplate;
        this.groupRepository = groupRepository;
    }

    @Transactional
    public SearchQueryResult<User> listUsers(SearchQueryPagination pagination) {
        String query = """
                SELECT *
                FROM sys_user
                ORDER BY sys_user.workday_id LIMIT ? OFFSET ?""";

        return new SearchQueryResult<>(jdbcTemplate.query(query, new UserMapper(), pagination.getLimit(), pagination.getOffset()), pagination.getPage(), getTotalUserCount(), pagination.getLimit());

    }

    private Integer getTotalUserCount() {
        String query = "SELECT COUNT(*) FROM sys_user";
        return jdbcTemplate.queryForObject(query, Integer.class);
    }

    private List<Group> getGroupMemberships(int id) {

        String query = "SELECT * FROM sys_group WHERE id IN (SELECT group_id FROM sys_user_group_mapping WHERE user_id = ?)";

        return jdbcTemplate.query(query, (rs, rowNum) -> {
            var group = new Group();

            group.setName(rs.getString("group_name"));
            group.setDescription(rs.getString("group_description"));

            return group;
        }, id);
    }


    @Transactional
    public Integer update(User user) {

        Integer userId = getUserIdByWorkdayId(user.getWorkdayId());

        List<Integer> groupIds = groupRepository.findGroupIds(user.getGroups().stream().map(Group::getName).toList());

        if (userId == null) {
            KeyHolder keyHolder = new GeneratedKeyHolder();
            jdbcTemplate.update(connection -> {
                PreparedStatement ps = connection.prepareStatement(
                        "INSERT INTO sys_user (workday_id, email, firstname, lastname, is_active) " +
                                "VALUES (?, ?, ?, ?, ?)",
                        Statement.RETURN_GENERATED_KEYS
                );
                ps.setString(1, user.getWorkdayId());
                ps.setString(2, user.getEmail());
                ps.setString(3, user.getFirstName());
                ps.setString(4, user.getLastName());
                ps.setBoolean(5, user.getActive());
                return ps;
            }, keyHolder);

            userId = Objects.requireNonNull(keyHolder.getKey()).intValue();
        } else {
            String query = """
                    UPDATE sys_user SET email = ?, firstname = ?, lastname = ?, workday_id = ?, is_active = ? WHERE id = ?""";

            jdbcTemplate.update(query, user.getEmail(), user.getFirstName(), user.getLastName(), user.getWorkdayId(), user.getActive(), userId);
        }

        updateUserGroupMappings(userId, groupIds);

        return userId;

    }

    @Transactional
    public Integer count() {
        return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM sys_user", Integer.class);
    }

    private void updateUserGroupMappings(Integer userId, List<Integer> groups) {

        // Handle empty groups list case
        if (groups.isEmpty()) {
            // Delete all mappings for this user
            jdbcTemplate.update("DELETE FROM sys_user_group_mapping WHERE user_id = ?", userId);
            return;
        } else
        {
            for (Integer groupId : groups) {
                jdbcTemplate.update(
                        "INSERT IGNORE INTO sys_user_group_mapping (user_id, group_id) VALUES (?, ?)",
                        userId, groupId
                );
            }
        }

        String placeholders = String.join(",", Collections.nCopies(groups.size(), "?"));
        String query = "DELETE FROM sys_user_group_mapping WHERE user_id = ? AND group_id NOT IN (" + placeholders + ")";

        jdbcTemplate.update(
                query,
                ps -> {
                    ps.setInt(1, userId);
                    for (int index = 0; index < groups.size(); index++) {
                        ps.setInt(index + 2, groups.get(index));  // Parameters start at index 2
                    }
                }
        );
    }


    @Transactional
    public Integer getUserIdByWorkdayId(String workdayId) {
        List<Integer> results = jdbcTemplate.query("SELECT id FROM sys_user WHERE workday_id = ?",
                (rs, rowNum) -> rs.getInt("id"),
                workdayId);

        return results.isEmpty() ? null : results.getFirst();
    }

    @Transactional
    public Optional<User> getByWorkdayId(String workdayId) {
        List<User> results = jdbcTemplate.query("SELECT * FROM sys_user WHERE workday_id = ?",
                new UserMapper(),
                workdayId);

        return results.isEmpty() ? Optional.empty() : Optional.of(results.getFirst());
    }

    @Transactional
    public User getById(Integer id) {
        String query = """
                SELECT *
                FROM sys_user
                WHERE id = ?""";


        var user = jdbcTemplate.query(query, new UserMapper(), id);

        return user.isEmpty() ? null : user.getFirst();
    }

    @Transactional
    public User getByEmail(String email) {
        String query = """
                SELECT *
                FROM sys_user
                WHERE email = ?""";

        var user = jdbcTemplate.query(query, new UserMapper(), email);

        return user.isEmpty() ? null : user.getFirst();
    }

    private class UserMapper implements RowMapper<User> {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {

            var user = new User();

            int id = rs.getInt("id");

            user.setId(id);
            user.setActive(rs.getBoolean("is_active"));
            user.setEmail(rs.getString("email"));
            user.setFirstName(rs.getString("firstname"));
            user.setLastName(rs.getString("lastname"));
            user.setWorkdayId(rs.getString("workday_id"));

            user.setGroups(getGroupMemberships(id));

            return user;

        }
    }
}
