package com.rjlresaka.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;

import com.rjlresaka.model.FriendRequest;
import com.rjlresaka.model.User;
import com.rjlresaka.util.DatabaseConnection;

public class FriendDAO {

    public List<User> findFriends(int currentUserId, ServletContext context) throws SQLException, ClassNotFoundException {
        String sql = "SELECT u.* FROM friends f "
                + "INNER JOIN users u ON u.id = CASE WHEN f.user_one_id = ? THEN f.user_two_id ELSE f.user_one_id END "
                + "WHERE f.user_one_id = ? OR f.user_two_id = ? "
                + "ORDER BY u.full_name ASC";
        List<User> friends = new ArrayList<User>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, currentUserId);
            statement.setInt(3, currentUserId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    User friend = mapUser(rs);
                    friend.setFriend(true);
                    friends.add(friend);
                }
            }
        }
        return friends;
    }

    public List<User> findPeople(int currentUserId, ServletContext context) throws SQLException, ClassNotFoundException {
        String sql = "SELECT u.*, "
                + "CASE WHEN fr_out.id IS NULL THEN 0 ELSE 1 END AS request_sent, "
                + "CASE WHEN fr_in.id IS NULL THEN 0 ELSE 1 END AS request_received "
                + "FROM users u "
                + "LEFT JOIN friend_requests fr_out ON fr_out.sender_id = ? AND fr_out.receiver_id = u.id AND fr_out.status = 'pending' "
                + "LEFT JOIN friend_requests fr_in ON fr_in.sender_id = u.id AND fr_in.receiver_id = ? AND fr_in.status = 'pending' "
                + "WHERE u.id <> ? "
                + "AND NOT EXISTS ("
                + "    SELECT 1 FROM friends f WHERE f.user_one_id = LEAST(?, u.id) AND f.user_two_id = GREATEST(?, u.id)"
                + ") "
                + "ORDER BY CASE WHEN fr_in.id IS NULL THEN 1 ELSE 0 END ASC, u.full_name ASC";
        List<User> users = new ArrayList<User>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, currentUserId);
            statement.setInt(3, currentUserId);
            statement.setInt(4, currentUserId);
            statement.setInt(5, currentUserId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    User user = mapUser(rs);
                    user.setRequestSent(rs.getInt("request_sent") == 1);
                    user.setRequestReceived(rs.getInt("request_received") == 1);
                    users.add(user);
                }
            }
        }
        return users;
    }

    public List<FriendRequest> findPendingRequestsReceived(int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT fr.id, fr.sender_id, fr.receiver_id, fr.created_at, "
                + "u.full_name, u.username, u.avatar_color "
                + "FROM friend_requests fr "
                + "INNER JOIN users u ON u.id = fr.sender_id "
                + "WHERE fr.receiver_id = ? AND fr.status = 'pending' "
                + "ORDER BY fr.created_at DESC";
        List<FriendRequest> requests = new ArrayList<FriendRequest>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    FriendRequest request = new FriendRequest();
                    request.setId(rs.getInt("id"));
                    request.setSenderId(rs.getInt("sender_id"));
                    request.setReceiverId(rs.getInt("receiver_id"));
                    request.setSenderName(rs.getString("full_name"));
                    request.setSenderUsername(rs.getString("username"));
                    request.setSenderAvatarColor(rs.getString("avatar_color"));
                    request.setCreatedAt(rs.getTimestamp("created_at"));
                    requests.add(request);
                }
            }
        }
        return requests;
    }

    public void sendRequest(int senderId, int receiverId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        if (senderId <= 0 || receiverId <= 0 || senderId == receiverId) {
            return;
        }

        try (Connection connection = DatabaseConnection.getConnection(context)) {
            connection.setAutoCommit(false);
            try {
                if (areFriends(connection, senderId, receiverId)) {
                    connection.commit();
                    return;
                }

                Integer reversePendingId = findPendingRequestId(connection, receiverId, senderId);
                if (reversePendingId != null) {
                    updateRequestStatus(connection, reversePendingId.intValue(), "accepted");
                    createFriendship(connection, senderId, receiverId);
                    connection.commit();
                    return;
                }

                if (findPendingRequestId(connection, senderId, receiverId) != null) {
                    connection.commit();
                    return;
                }

                String insert = "INSERT INTO friend_requests (sender_id, receiver_id, status) VALUES (?, ?, 'pending')";
                try (PreparedStatement statement = connection.prepareStatement(insert)) {
                    statement.setInt(1, senderId);
                    statement.setInt(2, receiverId);
                    statement.executeUpdate();
                }
                connection.commit();
            } catch (SQLException exception) {
                connection.rollback();
                throw exception;
            } finally {
                connection.setAutoCommit(true);
            }
        }
    }

    public void respondToRequest(int requestId, int currentUserId, boolean accept, ServletContext context)
            throws SQLException, ClassNotFoundException {
        try (Connection connection = DatabaseConnection.getConnection(context)) {
            connection.setAutoCommit(false);
            try {
                String select = "SELECT sender_id, receiver_id FROM friend_requests WHERE id = ? AND receiver_id = ? AND status = 'pending' LIMIT 1";
                try (PreparedStatement statement = connection.prepareStatement(select)) {
                    statement.setInt(1, requestId);
                    statement.setInt(2, currentUserId);
                    try (ResultSet rs = statement.executeQuery()) {
                        if (!rs.next()) {
                            connection.commit();
                            return;
                        }
                        int senderId = rs.getInt("sender_id");
                        int receiverId = rs.getInt("receiver_id");
                        updateRequestStatus(connection, requestId, accept ? "accepted" : "declined");
                        if (accept) {
                            createFriendship(connection, senderId, receiverId);
                        }
                    }
                }
                connection.commit();
            } catch (SQLException exception) {
                connection.rollback();
                throw exception;
            } finally {
                connection.setAutoCommit(true);
            }
        }
    }

    private Integer findPendingRequestId(Connection connection, int senderId, int receiverId) throws SQLException {
        String sql = "SELECT id FROM friend_requests WHERE sender_id = ? AND receiver_id = ? AND status = 'pending' LIMIT 1";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, senderId);
            statement.setInt(2, receiverId);
            try (ResultSet rs = statement.executeQuery()) {
                return rs.next() ? Integer.valueOf(rs.getInt("id")) : null;
            }
        }
    }

    private boolean areFriends(Connection connection, int firstUserId, int secondUserId) throws SQLException {
        String sql = "SELECT id FROM friends WHERE user_one_id = ? AND user_two_id = ? LIMIT 1";
        int userOneId = Math.min(firstUserId, secondUserId);
        int userTwoId = Math.max(firstUserId, secondUserId);
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, userOneId);
            statement.setInt(2, userTwoId);
            try (ResultSet rs = statement.executeQuery()) {
                return rs.next();
            }
        }
    }

    private void createFriendship(Connection connection, int firstUserId, int secondUserId) throws SQLException {
        int userOneId = Math.min(firstUserId, secondUserId);
        int userTwoId = Math.max(firstUserId, secondUserId);
        if (areFriends(connection, userOneId, userTwoId)) {
            return;
        }
        String sql = "INSERT INTO friends (user_one_id, user_two_id) VALUES (?, ?)";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, userOneId);
            statement.setInt(2, userTwoId);
            statement.executeUpdate();
        }
    }

    private void updateRequestStatus(Connection connection, int requestId, String status) throws SQLException {
        String sql = "UPDATE friend_requests SET status = ?, responded_at = NOW() WHERE id = ?";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setString(1, status);
            statement.setInt(2, requestId);
            statement.executeUpdate();
        }
    }

    private User mapUser(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getInt("id"));
        user.setFullName(rs.getString("full_name"));
        user.setUsername(rs.getString("username"));
        user.setEmail(rs.getString("email"));
        user.setPasswordHash(rs.getString("password_hash"));
        user.setAvatarColor(rs.getString("avatar_color"));
        user.setBio(rs.getString("bio"));
        user.setCreatedAt(rs.getTimestamp("created_at"));
        user.setUpdatedAt(rs.getTimestamp("updated_at"));
        return user;
    }
}
