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

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.ios.dep.beans.Account;
import org.wso2.carbon.device.mgt.ios.dep.beans.DeviceSerials;
import org.wso2.carbon.device.mgt.ios.dep.beans.DevicesWithSyncInfo;
import org.wso2.carbon.device.mgt.ios.dep.beans.ProfileAssignmentStatuses;
import org.wso2.carbon.device.mgt.ios.dep.beans.DevicesWithProfileAssignmentStatus;
import org.wso2.carbon.device.mgt.ios.dep.beans.Device;
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.DeviceProfile;
import org.wso2.carbon.device.mgt.ios.dep.beans.ProfileWithDevices;
import org.wso2.carbon.device.mgt.ios.dep.exception.DEPServerTokenException;
import org.wso2.carbon.device.mgt.ios.services.DEPService;
import org.wso2.carbon.device.mgt.ios.dep.beans.PaginationDetails;
import org.wso2.carbon.device.mgt.ios.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.ios.dep.exception.AppleDEPConnectorException;
import org.wso2.carbon.device.mgt.ios.dep.utils.EndpointUtils;

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

/**
 * iOS COPE device enrollment REST-API implementation.
 */
@Consumes({MediaType.APPLICATION_JSON})
@Produces({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("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.");
            }
            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 = "Error occurred while retrieving Apple DEP device 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/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();
        }
        if (cursor == null || cursor.isEmpty()) {
            String message = "Apple DEP sync devices cursor cannot be empty.";
            log.error(message);
            return Response.status(400).entity(
                    new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
        }
        try {
            EndpointUtils.checkTokenExpiration();
            PaginationDetails syncDevice = new PaginationDetails();
            syncDevice.setCursor(cursor);
            syncDevice.setLimit(limit);
            DevicesWithSyncInfo syncDeviceDetails = EndpointUtils.syncDevices(syncDevice);
            if (log.isDebugEnabled()) {
                log.debug("Successfully invoked Apple DEP sync devices.");
            }
            return Response.status(Response.Status.OK).entity(syncDeviceDetails).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();
        }
    }

    @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 (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();
        }
    }

    @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 (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();
        }
    }

    @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.size() < 1) {
            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.");
            }
            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();
        }
    }

    @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.size() < 1) {
            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();
        }
    }
}
