package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.company.CompanyActionRequestDto;
import ru.soune.nocopy.dto.company.CompanyResponseDto;
import ru.soune.nocopy.entity.company.Company;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.ResourceNotFoundException;
import ru.soune.nocopy.exception.ValidationException;
import ru.soune.nocopy.handler.validator.RegRequestValidator;
import ru.soune.nocopy.service.CompanyService;

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

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

    private final CompanyService companyService;
    
    private final RegRequestValidator regRequestValidator;
    
    private final ObjectMapper objectMapper;

    @Override
    public BaseResponse handle(BaseRequest request) {
        try {
            CompanyActionRequestDto actionRequest = objectMapper.convertValue(
                    request.getMessageBody(),
                    CompanyActionRequestDto.class
            );

            String action = actionRequest.getAction();

            return switch (action) {
                case "create" -> handleCreate(actionRequest, request.getMsgId());
                case "update" -> handleUpdate(actionRequest, request.getMsgId());
                case "delete" -> handleDelete(actionRequest, request.getMsgId());
                case "getCompany" -> handleGet(actionRequest, request.getMsgId());
                case "getAll" -> handleGetAll(actionRequest, request.getMsgId());
                case "addUser" -> handleAddUser(actionRequest, request.getMsgId());
                case "removeUser" -> handleRemoveUser(actionRequest, request.getMsgId());
                case "getUsers" -> handleGetUsers(actionRequest, request.getMsgId());
                case "search" -> handleSearch(actionRequest, request.getMsgId());
                case "countUser" -> handleCountUsers(actionRequest, request.getMsgId());
                default -> new BaseResponse(
                        request.getMsgId(),
                        MessageCode.INVALID_ACTION.getCode(),
                        "Invalid action: " + action,
                        null
                );
            };

        } catch (ValidationException e) {
            Map<String, String> errors = e.getBindingResult().getFieldErrors().stream()
                    .collect(Collectors.toMap(
                            error -> error.getField(),
                            error -> error.getDefaultMessage() != null ? error.getDefaultMessage() : "Validation error"
                    ));

            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.VALIDATION_ERROR.getCode(),
                    "Validation failed",
                    Map.of("errors", errors)
            );

        } catch (ResourceNotFoundException e) {
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.COMPANY_NOT_FOUND.getCode(),
                    e.getMessage(),
                    null
            );

        } catch (IllegalArgumentException e) {
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.VALIDATION_ERROR.getCode(),
                    e.getMessage(),
                    null
            );

        } catch (Exception e) {
            log.error("Error handling company action", e);
            return new BaseResponse(
                    request.getMsgId(),
                    MessageCode.INTERNAL_ERROR.getCode(),
                    "Error processing request: " + e.getMessage(),
                    null
            );
        }
    }

    private BaseResponse handleCreate(CompanyActionRequestDto request, Integer msgId) {
        if (companyService.companyExistsByName(request.getCompanyName())) {
            return new BaseResponse(
                    msgId,
                    MessageCode.COMPANY_ALREADY_EXISTS.getCode(),
                    "Company with this name already exists",
                    null
            );
        }

        validateCompanyCreateData(request);

        Company company = companyService.addCompany(
                request.getCompanyName(),
                getStringParam(request, "phone"),
                getStringParam(request, "address"),
                getStringParam(request, "email")
        );

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Company created successfully",
                mapToResponseDto(company)
        );
    }

    private BaseResponse handleUpdate(CompanyActionRequestDto request, Integer msgId) {
        if (request.getCompanyId() == null) {
            throw new IllegalArgumentException("Company ID is required for update");
        }

        Map<String, Object> companyData = request.getCompanyData();
        if (companyData == null) {
            companyData = new HashMap<>();
        }

        Company company = companyService.updateCompany(
                request.getCompanyId(),
                (String) companyData.get("companyName"),
                (String) companyData.get("phone"),
                (String) companyData.get("address"),
                (String) companyData.get("email")
        );

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Company updated successfully",
                mapToResponseDto(company)
        );
    }

    private BaseResponse handleDelete(CompanyActionRequestDto request, Integer msgId) {
        if (request.getCompanyId() == null) {
            throw new IllegalArgumentException("Company ID is required for deletion");
        }

        companyService.deleteCompany(request.getCompanyId());

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Company deleted successfully",
                Map.of("deletedCompanyId", request.getCompanyId())
        );
    }

    private BaseResponse handleGet(CompanyActionRequestDto request, Integer msgId) {
        Company company;

        if (request.getCompanyId() != null) {
            company = companyService.getCompanyById(request.getCompanyId());
        } else if (request.getCompanyName() != null) {
            company = companyService.getCompanyByName(request.getCompanyName());
        } else {
            throw new IllegalArgumentException("Either companyId or companyName is required");
        }

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Company retrieved successfully",
                mapToResponseDto(company)
        );
    }

    private BaseResponse handleGetAll(CompanyActionRequestDto request, Integer msgId) {
        List<Company> companies;

        if (request.getPageable() != null) {
            Pageable pageable = createPageable(request.getPageable());
            Page<Company> companyPage = companyService.getAllCompanies(pageable);

            List<CompanyResponseDto> companyDtos = companyPage.getContent().stream()
                    .map(this::mapToResponseDto)
                    .collect(Collectors.toList());

            return new BaseResponse(
                    msgId,
                    MessageCode.SUCCESS.getCode(),
                    "Companies retrieved successfully",
                    Map.of(
                            "companies", companyDtos,
                            "totalElements", companyPage.getTotalElements(),
                            "totalPages", companyPage.getTotalPages(),
                            "currentPage", companyPage.getNumber(),
                            "pageSize", companyPage.getSize()
                    )
            );
        } else {
            companies = companyService.getAllCompanies();
            List<CompanyResponseDto> companyDtos = companies.stream()
                    .map(this::mapToResponseDto)
                    .collect(Collectors.toList());

            return new BaseResponse(
                    msgId,
                    MessageCode.SUCCESS.getCode(),
                    "Companies retrieved successfully",
                    Map.of("companies", companyDtos)
            );
        }
    }

    private BaseResponse handleAddUser(CompanyActionRequestDto request, Integer msgId) {
        if (request.getCompanyId() == null || request.getUserId() == null) {
            throw new IllegalArgumentException("Company ID and User ID are required");
        }

        companyService.addUserToCompany(request.getCompanyId(), request.getUserId());

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "User added to company successfully",
                Map.of(
                        "companyId", request.getCompanyId(),
                        "userId", request.getUserId()
                )
        );
    }

    private BaseResponse handleRemoveUser(CompanyActionRequestDto request, Integer msgId) {
        if (request.getUserId() == null) {
            throw new IllegalArgumentException("User ID is required");
        }

        companyService.deleteUserFromCompany(request.getUserId());

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "User removed from company successfully",
                Map.of("userId", request.getUserId())
        );
    }

    private BaseResponse handleGetUsers(CompanyActionRequestDto request, Integer msgId) {
        if (request.getCompanyId() == null) {
            throw new IllegalArgumentException("Company ID is required");
        }

        List<User> users = companyService.getCompanyUsers(request.getCompanyId());

        List<Map<String, Object>> userDtos = users.stream()
                .map(user -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("id", user.getId());
                    map.put("email", user.getEmail());
                    map.put("phone", user.getPhone());
                    map.put("fullName", user.getFullName());
                    return map;
                }).toList();

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Company users retrieved successfully",
                Map.of(
                        "companyId", request.getCompanyId(),
                        "users", userDtos,
                        "count", userDtos.size()
                )
        );
    }

    private BaseResponse handleSearch(CompanyActionRequestDto request, Integer msgId) {
        String searchTerm = getStringParam(request, "name");
        if (searchTerm == null) {
            throw new IllegalArgumentException("Search term is required");
        }

        List<Company> companies = companyService.searchCompaniesByName(searchTerm);
        List<CompanyResponseDto> companyDtos = companies.stream()
                .map(this::mapToResponseDto)
                .collect(Collectors.toList());

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "Companies search completed",
                Map.of(
                        "searchTerm", searchTerm,
                        "companies", companyDtos,
                        "count", companyDtos.size()
                )
        );
    }

    private BaseResponse handleCountUsers(CompanyActionRequestDto request, Integer msgId) {
        if (request.getCompanyId() == null) {
            throw new IllegalArgumentException("Company ID is required");
        }

        long count = companyService.countCompanyUsers(request.getCompanyId());

        return new BaseResponse(
                msgId,
                MessageCode.SUCCESS.getCode(),
                "User count retrieved",
                Map.of(
                        "companyId", request.getCompanyId(),
                        "userCount", count
                )
        );
    }

    private void validateCompanyCreateData(CompanyActionRequestDto request) throws ValidationException {
        BindingResult bindingResult = new BeanPropertyBindingResult(request, "companyRequest");

        if (request.getCompanyName() != null) {
            regRequestValidator.validateCompanyName(request.getCompanyName(), bindingResult);
        }

        String email = getStringParam(request, "email");
        if (email != null) {
            regRequestValidator.validateEmail(email, bindingResult);
        }

        String phone = getStringParam(request, "phone");
        if (phone != null) {
            regRequestValidator.validatePhone(phone, bindingResult);
        }

        if (bindingResult.hasErrors()) {
            throw new ValidationException(bindingResult, null);
        }
    }

    private String getStringParam(CompanyActionRequestDto request, String paramName) {
        if (request.getCompanyData() != null) {
            Object value = request.getCompanyData().get(paramName);
            return value != null ? value.toString() : null;
        }

        Map<String, Object> searchParams = request.getSearchParams();
        if (searchParams != null) {
            Object value = searchParams.get(paramName);
            return value != null ? value.toString() : null;
        }

        return null;
    }

    private CompanyResponseDto mapToResponseDto(Company company) {
        CompanyResponseDto dto = new CompanyResponseDto();
        dto.setId(company.getId());
        dto.setCompanyName(company.getCompanyName());
        dto.setPhone(company.getPhone());
        dto.setAddress(company.getAddress());
        dto.setEmail(company.getEmail());
        dto.setRegisterDate(company.getRegisterDate());
        dto.setUpdateDate(company.getUpdateDate());

        try {
            dto.setUserCount(companyService.countCompanyUsers(company.getId()));
        } catch (Exception e) {
            dto.setUserCount(0);
        }

        return dto;
    }

    private Pageable createPageable(CompanyActionRequestDto.PageableParams params) {
        Sort sort = Sort.unsorted();

        if (params.getSort() != null) {
            Sort.Direction direction = params.getDirection() != null &&
                    "desc".equalsIgnoreCase(params.getDirection()) ?
                    Sort.Direction.DESC : Sort.Direction.ASC;
            sort = Sort.by(direction, params.getSort());
        }

        return PageRequest.of(
                params.getPage(),
                params.getSize(),
                sort
        );
    }
}
