package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse;

import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.user.UserAdditionalInfoBody;

import ru.soune.nocopy.entity.user.UserAdditionalInfo;
import ru.soune.nocopy.entity.user.UserAdditionalInfoAnswer;
import ru.soune.nocopy.entity.user.UserAdditionalInfoListAnswer;
import ru.soune.nocopy.exception.UserAdditionalInfoNotFound;
import ru.soune.nocopy.service.user.AdditionalInfoService;


import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
@Slf4j
@RequiredArgsConstructor
public class UserInfoHandler implements RequestHandler {

    private final AdditionalInfoService additionalInfoService;
    private final ObjectMapper objectMapper;

    @Override
    public BaseResponse handle(BaseRequest request) throws Exception {
        try {
            UserAdditionalInfoBody body = objectMapper.convertValue(request.getMessageBody(),
                    UserAdditionalInfoBody.class);

            log.info("Processing UserInfoHandler with action: {}", body.getAction());

            return switch (body.getAction()) {
                case "getAll" -> getAllAdditionalInfos(request);
                case "getById" -> getAdditionalInfoById(request, body.getUserId());
                case "update" -> updateAdditionalInfo(request, body);
                case "delete" -> deleteAdditionalInfo(request, body.getUserId());
                default -> new BaseResponse(
                        request.getMsgId(),
                        MessageCode.INVALID_ACTION.getCode(),
                        "Invalid action: " + body.getAction(),
                        null
                );
            };
        } catch (IllegalArgumentException e) {
            log.error("Validation error in UserInfoHandler", e);
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.VALIDATION_ERROR.getCode(),
                    e.getMessage(),
                    null);
        } catch (UserAdditionalInfoNotFound e) {
            log.error("User additional info not found", e);
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.NOT_FOUND.getCode(),
                    e.getMessage(),
                    null);
        } catch (Exception e) {
            log.error("Unexpected error in UserInfoHandler", e);
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.INTERNAL_ERROR.getCode(),
                    "Internal server error: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse getAllAdditionalInfos(BaseRequest request) {
        log.info("Getting all additional infos");

        List<UserAdditionalInfo> infos = additionalInfoService.getAllAdditionalInfos();

        List<UserAdditionalInfoAnswer> answerItems = infos.stream()
                .map(this::convertToAnswer)
                .collect(Collectors.toList());

        UserAdditionalInfoListAnswer listAnswer = UserAdditionalInfoListAnswer.builder()
                .items(answerItems)
                .count(answerItems.size())
                .build();

        return new BaseResponse(
                request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                listAnswer
        );
    }

    private BaseResponse getAdditionalInfoById(BaseRequest request, Long userId) {
        log.info("Getting additional info for user: {}", userId);

        if (userId == null) {
            throw new IllegalArgumentException("User ID is required for getById action");
        }

        UserAdditionalInfo info = additionalInfoService.getAdditionalInfoByUser(userId);

        if (info == null) {
            throw new UserAdditionalInfoNotFound(
                    String.format("User additional info not found for userId: %d", userId)
            );
        }

        UserAdditionalInfoAnswer answer = convertToAnswer(info);

        return new BaseResponse(
                request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                answer
        );
    }

    private BaseResponse updateAdditionalInfo(BaseRequest request, UserAdditionalInfoBody body) {
        log.info("Updating additional info for user: {}", body.getUserId());

        if (body.getUserId() == null) {
            throw new IllegalArgumentException("User ID is required for update action");
        }

        validateIpAddress(body.getIpAddress());
        validateUserAgent(body.getUserAgent());

        Optional<UserAdditionalInfo> updatedInfo = additionalInfoService.updateAdditionalInfo(
                body.getUserId(),
                body.getIpAddress(),
                body.getUserAgent()
        );

        if (updatedInfo.isEmpty()) {
            throw new UserAdditionalInfoNotFound(
                    String.format("User additional info not found for userId: %d", body.getUserId())
            );
        }

        UserAdditionalInfoAnswer answer = convertToAnswer(updatedInfo.get());

        return new BaseResponse(
                request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                answer
        );
    }

    private BaseResponse deleteAdditionalInfo(BaseRequest request, Long userId) {
        log.info("Deleting additional info for user: {}", userId);

        if (userId == null) {
            throw new IllegalArgumentException("User ID is required for delete action");
        }

        UserAdditionalInfo existingInfo = additionalInfoService.getAdditionalInfoByUser(userId);

        if (existingInfo == null) {
            throw new UserAdditionalInfoNotFound(
                    String.format("User additional info not found for userId: %d", userId)
            );
        }

        additionalInfoService.deleteAdditionalInfo(userId);

        return new BaseResponse(
                request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                Map.of("deleted", true, "user_id", userId)
        );
    }

    private UserAdditionalInfoAnswer convertToAnswer(UserAdditionalInfo info) {
        return UserAdditionalInfoAnswer.builder()
                .id(info.getId())
                .userId(info.getUser() != null ? info.getUser().getId() : null)
                .ipAddress(info.getIpAddress())
                .userAgent(info.getUserAgent())
                .registrationDate(info.getRegistrationDate())
                .build();
    }

    private void validateIpAddress(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            throw new IllegalArgumentException("IP address is required for update action");
        }

        String ipv4Pattern =
                "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";

        if (!ip.trim().matches(ipv4Pattern)) {
            log.warn("Invalid IP address format: {}", ip);
            throw new IllegalArgumentException("Invalid IP address format");
        }
    }

    private void validateUserAgent(String userAgent) {
        if (userAgent == null || userAgent.trim().isEmpty()) {
            throw new IllegalArgumentException("User agent is required for update action");
        }

        if (userAgent.length() > 500) {
            throw new IllegalArgumentException("User agent must not exceed 500 characters");
        }
    }
}
