package org.wso2.carbon.device.mgt.ios.services.impl;

import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.ios.beans.DEPDeviceList;
import org.wso2.carbon.device.mgt.ios.beans.DEPProfileList;
import org.wso2.carbon.device.mgt.ios.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.ios.dep.DEPConstants;
import org.wso2.carbon.device.mgt.ios.dep.beans.Account;
import org.wso2.carbon.device.mgt.ios.dep.beans.Device;
import org.wso2.carbon.device.mgt.ios.dep.beans.DeviceDetails;
import org.wso2.carbon.device.mgt.ios.dep.beans.DeviceSerials;
import org.wso2.carbon.device.mgt.ios.dep.beans.Profile;
import org.wso2.carbon.device.mgt.ios.dep.beans.DeviceDetailsFilter;
import org.wso2.carbon.device.mgt.ios.dep.beans.ProfileAssignmentStatuses;
import org.wso2.carbon.device.mgt.ios.dep.beans.DeviceProfile;
import org.wso2.carbon.device.mgt.ios.dep.beans.ProfileAssignmentStatus;
import org.wso2.carbon.device.mgt.ios.dep.beans.ProfileWithDevices;
import org.wso2.carbon.device.mgt.ios.dep.beans.DEPDeviceUser;
import org.wso2.carbon.device.mgt.ios.dep.beans.DevicesWithProfileAssignmentStatus;
import org.wso2.carbon.device.mgt.ios.dep.beans.PaginationDetails;
import org.wso2.carbon.device.mgt.ios.dep.exception.AppleDEPConnectorException;
import org.wso2.carbon.device.mgt.ios.dep.exception.DEPServerTokenException;
import org.wso2.carbon.device.mgt.ios.dep.utils.EndpointUtils;
import org.wso2.carbon.device.mgt.ios.payload.exception.ProfileConfigurationException;
import org.wso2.carbon.device.mgt.ios.services.DEPService;
import org.wso2.carbon.device.mgt.ios.services.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.ios.util.DEPUtils;
import org.wso2.carbon.device.mgt.ios.util.IOSServiceUtils;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dto.DEPDevice;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dto.DEPProfile;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;

/**
 * iOS COPE device enrollment REST-API implementation.
 */
@Path("/dep")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class DEPServiceImpl implements DEPService {

    private static final Log log = LogFactory.getLog(DEPServiceImpl.class);

    @GET
    @Path("/account-details")
    public Response getAccountDetails() {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving account details from Apple DEP portal.");
        }
        try {
            EndpointUtils.checkTokenExpiration();
            Account accountDetails = EndpointUtils.getAccountDetails();
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP account details.");
            }
            return Response.status(Response.Status.OK).entity(accountDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while retrieving Apple DEP account details.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while retrieving Apple DEP account details.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @GET
    @Path("/devices")
    public Response getDevices(@QueryParam("limit") int limit, @QueryParam("offset") int offset,
                               @QueryParam("serial") String serial, @QueryParam("model") String model,
                               @QueryParam("status") String status, @QueryParam("requireLogin") String requireLogin,
                               @QueryParam("requireAgent") String requireAgent) {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving iOS devices registered in Apple DEP portal with parameters " +
                    "limit of " + limit + " and offset of " + offset + ".");
        }
        try {
            RequestValidationUtil.validatePaginationParameters(offset, limit);
            PaginationRequest request = new PaginationRequest(offset, limit);
            if (serial != null && !serial.isEmpty()) {
                request.setProperty(DEPDevice.PROP_SERIAL, serial);
            }
            if (model != null && !model.isEmpty()) {
                request.setProperty(DEPDevice.PROP_MODEL, model);
            }
            if (status != null && !status.isEmpty()) {
                request.setProperty(DEPDevice.PROP_STATUS, Boolean.getBoolean(status));
            }
            if (requireLogin != null && !requireLogin.isEmpty()) {
                request.setProperty(DEPDevice.PROP_REQUIRE_LOGIN, Boolean.getBoolean(requireLogin));
            }
            if (requireLogin != null && !requireLogin.isEmpty()) {
                request.setProperty(DEPDevice.PROP_REQUIRE_AGENT, Boolean.getBoolean(requireAgent));
            }
            PaginationResult result = IOSServiceUtils.getProfileService().getDEPDevices(request);
            DEPDeviceList devices = new DEPDeviceList();
            devices.setList((List<DEPDevice>)result.getData());
            devices.setCount(result.getRecordsTotal());
            if (log.isDebugEnabled()) {
                log.debug("Successfully fetched DEP devices from database.");
            }
            return Response.status(Response.Status.OK).entity(devices).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while syncing the DEP device to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @GET
    @Path("/devices/sync")
    public Response syncDevices(@QueryParam("limit") int limit, @QueryParam("cursor") String cursor) {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving iOS devices registered in Apple DEP portal with parameters " +
                    "limit of " + limit + " and cursor of " + cursor + ".");
        }
        if (limit < 0) {
            String message = "Number of devices to be retrieved cannot be a negative value.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (limit > 1000) {
            String message = "Number of devices to be retrieved cannot be greater than 1000.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            PaginationDetails paginationDetails = new PaginationDetails();
            paginationDetails.setLimit(limit);
            paginationDetails.setCursor(cursor);
            Device fetchDeviceDetails = EndpointUtils.getDevices(paginationDetails);
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP get devices.");
            }
            for (DeviceDetails deviceDetails : fetchDeviceDetails.getDevices()) {
                DEPDevice device = IOSServiceUtils.getProfileService().getDEPDevice(deviceDetails
                        .getSerialNumber());
                DEPDevice depDevice;
                if (device != null && StringUtils.isNotEmpty(device.getSerial())) {
                    // Device exists in the DB so it can be updated
                    depDevice = DEPUtils.convertToDevice(deviceDetails, device);
                    IOSServiceUtils.getProfileService().updateDEPDevice(depDevice);
                } else {
                    device = new DEPDevice();
                    device.setNeedBasicAuth(false);
                    device.setDepProfileId(null);
                    device.setUsername(null);
                    device.setAgentRequired(true);
                    depDevice = DEPUtils.convertToDevice(deviceDetails, device);
                    IOSServiceUtils.getProfileService().addDEPDevice(depDevice);
                }
            }
            return Response.status(Response.Status.OK).entity(fetchDeviceDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while retrieving Apple DEP device details.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while retrieving Apple DEP device details.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while syncing the DEP device to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @GET
    @Path("/devices/{deviceSerial}")
    public Response getDevice(@PathParam("deviceSerial") String deviceSerial) {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving individual iOS devices registered in Apple DEP portal with " +
                    "parameter device serial number of " + deviceSerial + ".");
        }
        if (deviceSerial == null || deviceSerial.isEmpty()) {
            String message = "Apple DEP device ID cannot be empty to retrieve device details.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            DeviceSerials deviceSerials = new DeviceSerials();
            deviceSerials.setDevices(new String[]{deviceSerial});
            DeviceDetailsFilter deviceDetails = EndpointUtils.getDevice(deviceSerials);
            String deviceStatus = deviceDetails.getResponseStatus();
            if (!(("SUCCESS").equals(deviceStatus))) {
                String message = "No device found for the provided Serial Number in Apple DEP portal.";
                log.error(message);
                return Response.status(404).entity(
                        new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
            }
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP individual device details.");
            }
            return Response.status(Response.Status.OK).entity(deviceDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while retrieving Apple DEP specific device details.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while retrieving Apple " +
                    "DEP specific device details.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @POST
    @Path("/devices/profile")
    public Response createProfile(DeviceProfile deviceProfile) {
        if (log.isDebugEnabled()) {
            log.debug("Creating Apple DEP profile in Apple DEP portal where the created profile " +
                    "can be applied to iOS devices.");
        }
        if (deviceProfile == null) {
            String message = "Apple DEP creating profile configurations cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getProfileName() == null || deviceProfile.getProfileName().isEmpty()) {
            String message = "Apple DEP creating profile name cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getUrl() == null || deviceProfile.getUrl().isEmpty()) {
            String message = "Apple DEP creating profile URL cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getOrgMagic() == null || deviceProfile.getOrgMagic().isEmpty()) {
            String message = "Apple DEP creating profile unique (org_magic) string cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            ProfileAssignmentStatuses defineProfileDetails = EndpointUtils.createProfile(deviceProfile);
            if (defineProfileDetails != null && defineProfileDetails.getProfileUuid() != null) {
                Gson gson = new Gson();
                String payload = gson.toJson(deviceProfile);
                DEPProfile depProfile = new DEPProfile();
                depProfile.setUUID(defineProfileDetails.getProfileUuid());
                depProfile.setProfileContent(payload);
                depProfile.setProfileName(deviceProfile.getProfileName());

                DEPProfile storedProfile = IOSServiceUtils.getProfileService().getDEPProfile
                        (defineProfileDetails.getProfileUuid());
                if (storedProfile == null || StringUtils.isEmpty(storedProfile.getId())) {
                    // If no stored profile in the DB, this is a new profile
                    int depProfileId = IOSServiceUtils.getProfileService().addDEPProfile(depProfile);
                    // Having devices in profiles is optional, therefore this is checked.
                    if (deviceProfile.getDevices() != null && deviceProfile.getDevices().length > 0) {
                        for (String serial : deviceProfile.getDevices()) {
                            // If there are devices also in the payload, these too must be added to
                            // database and synced with iProfile server.
                            DEPDevice device = IOSServiceUtils.getProfileService().getDEPDevice(serial);
                            // Check if the device already exist in the database.
                            if (device != null && StringUtils.isNotEmpty(device.getSerial())) {
                                // Device exists in the DB so it can be updated;
                                IOSServiceUtils.getProfileService().assignProfileToDevice
                                        (String.valueOf(device.getProfileUuid()), new String[]{device
                                                .getSerial()});
                            } else {
                                // This is a new device. The device must be synced and assigned.
                                DeviceSerials deviceSerials = new DeviceSerials();
                                deviceSerials.setDevices(new String[]{serial});
                                // Get the device details from iProfile server since the device
                                // details are not yet available in the DB.
                                DeviceDetailsFilter deviceDetails = EndpointUtils.getDevice(deviceSerials);
                                String deviceStatus = deviceDetails.getResponseStatus();
                                if (DEPConstants.PROFILE_ASSIGNED_SUCCESS.equals(deviceStatus)) {
                                    IOSServiceUtils.getProfileService().addDEPDevice(DEPUtils
                                            .convertDeviceDetailsFilterToDevice(deviceDetails, false,
                                                    null, depProfileId));
                                }
                            }
                        }
                    }
                }
            }
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP create profile.");
            }
            return Response.status(Response.Status.OK).entity(defineProfileDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to create profile for Apple DEP.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to create profile for Apple DEP.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while saving the DEP profile to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @PUT
    @Path("/devices/profile/{profileId}")
    public Response updateProfile(DeviceProfile deviceProfile, @PathParam("profileId") String
            profileId) {
        if (log.isDebugEnabled()) {
            log.debug("Creating Apple DEP profile in Apple DEP portal where the created profile " +
                    "can be applied to iOS devices.");
        }
        if (profileId == null || profileId.isEmpty()) {
            String message = "Apple DEP profile UUID cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile == null) {
            String message = "Apple DEP creating profile configurations cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getProfileName() == null || deviceProfile.getProfileName().isEmpty()) {
            String message = "Apple DEP creating profile name cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getUrl() == null || deviceProfile.getUrl().isEmpty()) {
            String message = "Apple DEP creating profile URL cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceProfile.getOrgMagic() == null || deviceProfile.getOrgMagic().isEmpty()) {
            String message = "Apple DEP creating profile unique (org_magic) string cannot be " +
                    "empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            DEPProfile depProfile = IOSServiceUtils.getProfileService().getDEPProfile(profileId);
            if (depProfile == null || StringUtils.isEmpty(depProfile.getId())) {
                return Response.status(404).entity(
                        new ErrorResponse.ErrorResponseBuilder().setMessage("The profile ID: " +
                                profileId + " could not be found.").build()).build();
            } else {
                // To begin with, the profile update request is posted to define profile endpoint of DEP, regardless
                // it exist or not.
                EndpointUtils.checkTokenExpiration();
                ProfileAssignmentStatuses defineProfileDetails = EndpointUtils.createProfile(deviceProfile);
                // Check if the backend returns a different profile UDID. Apple backend returns a different Id, only
                // if there is a change in the profile payload.
                if (defineProfileDetails != null && StringUtils.isNotEmpty(defineProfileDetails.getProfileUuid()) &&
                        !defineProfileDetails.getProfileUuid().equals(profileId)) {
                    DEPProfile storedProfile = IOSServiceUtils.getProfileService().getDEPProfile(profileId);
                    if (storedProfile != null && StringUtils.isNotEmpty(storedProfile.getId())) {
                        storedProfile.setUUID(defineProfileDetails.getProfileUuid());
                        storedProfile.setProfileName(deviceProfile.getProfileName());
                        Gson gson = new Gson();
                        String payload = gson.toJson(deviceProfile);
                        storedProfile.setProfileContent(payload);
                        IOSServiceUtils.getProfileService().updateDEPProfile(storedProfile);
                    }
                }
            }
            EndpointUtils.checkTokenExpiration();
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP create profile.");
            }
            return Response.status(Response.Status.OK).entity("Profile created").build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to create profile for Apple DEP.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to create profile for Apple DEP.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while saving the DEP profile to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @POST
    @Path("/devices/assign-profile")
    public Response assignProfile(ProfileWithDevices profileWithDevices) {
        if (log.isDebugEnabled()) {
            log.debug("Assigning created profile in Apple DEP portal to devices.");
        }
        if (profileWithDevices == null) {
            String message = "Apple DEP assigning profile configurations cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (profileWithDevices.getProfileUuid() == null ||
                profileWithDevices.getProfileUuid().isEmpty()) {
            String message = "Apple DEP assigning profile UUID cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (profileWithDevices.getDevices() == null || profileWithDevices.getDevices().length < 1) {
            String message = "Apple DEP devices to assign profile cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            ProfileAssignmentStatuses assignProfileDetails =
                    EndpointUtils.assignProfile(profileWithDevices);
            if (assignProfileDetails != null && assignProfileDetails.getProfileUuid() != null) {
                for (ProfileAssignmentStatus deviceStatus : assignProfileDetails.getDevices()) {
                    if (DEPConstants.PROFILE_ASSIGNED_SUCCESS.equalsIgnoreCase(deviceStatus
                            .getProfileAssignmentStatus())) {
                        IOSServiceUtils.getProfileService().assignProfileToDevice
                                (assignProfileDetails.getProfileUuid(), new String[]{deviceStatus
                                        .getSerialNumber()});
                    }
                }
            }
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP assign profile.");
            }
            return Response.status(Response.Status.OK).entity(assignProfileDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to assign profile for Apple DEP.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to assign profile for Apple DEP.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while saving the DEP profile to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/profiles")
    public Response getProfiles(@QueryParam("offset") int offset, @QueryParam("limit") int limit,
                                @QueryParam("name") String name, @QueryParam("department") String department,
                                @QueryParam("mandatory") String mandatory,
                                @QueryParam("removable") String removable) {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving all Apple DEP profile from local database.");
        }
        try {
            RequestValidationUtil.validatePaginationParameters(offset, limit);
            PaginationRequest request = new PaginationRequest(offset, limit);
            if (name != null && !name.isEmpty()) {
                request.setProperty(DEPProfile.PROP_NAME, name);
            }
            if (department != null && !department.isEmpty()) {
                request.setProperty(DEPProfile.PROP_DEPARTMENT, department);
            }
            if (mandatory != null && !mandatory.isEmpty()) {
                request.setProperty(DEPProfile.PROP_MANDATORY, Boolean.getBoolean(mandatory));
            }
            if (removable != null && !removable.isEmpty()) {
                request.setProperty(DEPProfile.PROP_REMOVABLE, Boolean.getBoolean(removable));
            }

            PaginationResult result = IOSServiceUtils.getProfileService().getDEPProfiles(request);
            DEPProfileList profiles = new DEPProfileList();
            profiles.setList((List<DEPProfile>)result.getData());
            profiles.setCount(result.getRecordsTotal());
            if (log.isDebugEnabled()) {
                log.debug("Successfully fetched profiles from database.");
            }
            return Response.status(Response.Status.OK).entity(profiles).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while saving the DEP profile to the database.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/profile/{profileId}")
    public Response getProfile(@PathParam("profileId") String profileId) {
        if (log.isDebugEnabled()) {
            log.debug("Retrieving Apple DEP profile from Apple DEP portal with parameter " +
                    "profile ID of " + profileId + ".");
        }
        if (profileId == null || profileId.isEmpty()) {
            String message = "Apple DEP profile UUID cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            Profile fetchProfileDetails = EndpointUtils.getProfile(profileId);
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP get profile.");
            }
            return Response.status(Response.Status.OK).entity(fetchProfileDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to retrieve profile from Apple DEP.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to retrieve" +
                    " profile from Apple DEP.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @POST
    @Path("/devices/remove-profile")
    public Response removeProfile(List<String> deviceSerialNumbers) {
        if (log.isDebugEnabled()) {
            log.debug("Removing Apple DEP profile from devices.");
        }
        if (deviceSerialNumbers == null || deviceSerialNumbers.isEmpty()) {
            String message = "Apple DEP devices to remove profile cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            DeviceSerials deviceSerials = new DeviceSerials();
            deviceSerials.setDevices(deviceSerialNumbers.toArray(new String[0]));
            DevicesWithProfileAssignmentStatus removeProfileDetails =
                    EndpointUtils.removeProfile(deviceSerials);
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP remove profile.");
            }
            for (ProfileAssignmentStatus deviceStatus : removeProfileDetails.getDevices()) {
                if (DEPConstants.PROFILE_ASSIGNED_SUCCESS.equalsIgnoreCase(deviceStatus
                        .getProfileAssignmentStatus())) {
                    IOSServiceUtils.getProfileService().removeProfileToDeviceAssignment(new String[]{deviceStatus
                            .getSerialNumber()});
                }
            }

            return Response.status(Response.Status.OK).entity(removeProfileDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to remove profile from devices.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to remove" +
                    " profile from devices.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error occurred while removing dep device to profile link.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @POST
    @Path("/devices/disown")
    public Response disownDevices(List<String> deviceSerialNumbers) {
        if (log.isDebugEnabled()) {
            log.debug("Disowning devices from Apple DEP portal.");
        }
        if (deviceSerialNumbers == null || deviceSerialNumbers.isEmpty()) {
            String message = "Device serial numbers to be disowned cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            DeviceSerials deviceSerials = new DeviceSerials();
            deviceSerials.setDevices(deviceSerialNumbers.toArray(new String[0]));
            DevicesWithProfileAssignmentStatus disownDeviceDetails =
                    EndpointUtils.disownDevices(deviceSerials);
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP disown deviceSerials.");
            }
            return Response.status(Response.Status.OK).entity(disownDeviceDetails).build();
        } catch (AppleDEPConnectorException e) {
            String message = "Error occurred while trying to disown devices from Apple DEP portal.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DEPServerTokenException e) {
            String message = "Server Token (.p7m format) error occurred while trying to disown " +
                    "devices from Apple DEP portal.";
            log.error(message, e);
            return Response.status(Response.Status.UNAUTHORIZED).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        } catch (DeviceManagementException e) {
            String message = "Error occurred while retrieving iOS DEP configuration from registry.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }

    @POST
    @Path("/devices/assign-user")
    public Response assignUser(DEPDeviceUser deviceUser) {
        if (log.isDebugEnabled()) {
            log.debug("Assigning a set of devices to a user.");
        }
        if (deviceUser == null) {
            String message = "Device and user details cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceUser.getUsername() == null ||
                deviceUser.getUsername().isEmpty()) {
            String message = "Username cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        if (deviceUser.getSerial() == null || deviceUser.getSerial().length < 1) {
            String message = "Serial numbers cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            IOSServiceUtils.getProfileService().updateDeviceUser(deviceUser.getUsername(), deviceUser.getSerial(),
                    deviceUser.getRequireBasicAuth(), deviceUser.getRequireAgentInstall());
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP assign profile.");
            }
            return Response.status(Response.Status.OK).build();
        } catch (ProfileConfigurationException e) {
            String message = "Error assigning user: " + deviceUser + " to devices provided.";
            log.error(message, e);
            return Response.serverError().entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
    }
}
