
package org.wso2.carbon.device.mgt.ios.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService;
import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException;
import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.ios.apns.exception.APNSException;
import org.wso2.carbon.device.mgt.ios.apns.service.PushNotificationService;
import org.wso2.carbon.device.mgt.ios.beans.ErrorListItem;
import org.wso2.carbon.device.mgt.ios.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.ios.core.exception.IOSEnrollmentException;
import org.wso2.carbon.device.mgt.ios.exception.BadRequestException;
import org.wso2.carbon.device.mgt.mobile.impl.ios.enrollment.IOSEnrollmentTokenService;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;

import javax.validation.ConstraintViolation;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class IOSServiceUtils {

    private static final String DEVICE_TYPE_IOS = "ios";
    public static final int MULTI_STATUS_HTTP_CODE = 207;
    public static final MediaType DEFAULT_CONTENT_TYPE = MediaType.APPLICATION_JSON_TYPE;
    private static final Log log = LogFactory.getLog(IOSServiceUtils.class);

    public static DeviceManagementProviderService getDeviceManagementService() throws DeviceManagementException {

        PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
        DeviceManagementProviderService deviceManagementService = (DeviceManagementProviderService)
                ctx.getOSGiService(DeviceManagementProviderService.class, null);

        if (deviceManagementService == null) {
            String msg = "DeviceManagementService is not initialized";
            log.error(msg);
            throw new DeviceManagementException(msg);
        }

        return deviceManagementService;
    }

    public static ApplicationManagementProviderService getApplicationManagementService()
            throws ApplicationManagementException {

        PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
        ApplicationManagementProviderService applicationManagementService =
                (ApplicationManagementProviderService)
                        ctx.getOSGiService(ApplicationManagementProviderService.class, null);

        if (applicationManagementService == null) {
            String msg = "ApplicationManagementProviderService is not initialized";
            log.error(msg);
            throw new ApplicationManagementException(msg);
        }

        return applicationManagementService;
    }

    public static PushNotificationService getPushNotificationService() throws APNSException {

        PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
        PushNotificationService pushNotificationService = (PushNotificationService)
                ctx.getOSGiService(PushNotificationService.class, null);

        if (pushNotificationService == null) {
            String msg = "PushNotificationService is not initialized";
            log.error(msg);
            throw new APNSException(msg);
        }

        return pushNotificationService;
    }

    public static IOSEnrollmentTokenService getEnrollmentTokenService() throws IOSEnrollmentException {

        PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
        IOSEnrollmentTokenService tokenService = (IOSEnrollmentTokenService)
                ctx.getOSGiService(IOSEnrollmentTokenService.class, null);

        if (tokenService == null) {
            String msg = "IOSEnrollmentTokenService is not initialized";
            log.error(msg);
            throw new IOSEnrollmentException(msg);
        }

        return tokenService;
    }

    public static MediaType getResponseMediaType(String acceptHeader) {

        MediaType responseMediaType;
        if (MediaType.WILDCARD.equals(acceptHeader)) {
            responseMediaType = DEFAULT_CONTENT_TYPE;
        } else {
            responseMediaType = MediaType.valueOf(acceptHeader);
        }

        return responseMediaType;
    }

    public static Response getOperationResponse(List<String> deviceIDs, Operation operation)
            throws DeviceManagementException, OperationManagementException, APNSException, InvalidDeviceException {
        if (deviceIDs == null || deviceIDs.size() == 0) {
            String errorMessage = "Device identifier list is empty";
            throw new BadRequestException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
        }

        DeviceIdentifier deviceIdentifier;
        List<DeviceIdentifier> deviceIdentifiers = new ArrayList<>();
        for (String deviceId : deviceIDs) {
            deviceIdentifier = new DeviceIdentifier();
            deviceIdentifier.setId(deviceId);
            deviceIdentifier.setType(IOSServiceUtils.DEVICE_TYPE_IOS);
            deviceIdentifiers.add(deviceIdentifier);
        }
        Activity activity = getDeviceManagementService().addOperation(
                DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS, operation, deviceIdentifiers);

       // PushNotificationSenderUtils pushNotificationSenderUtils = new PushNotificationSenderUtils();
       // pushNotificationSenderUtils.contactAPNS(deviceIDHolder.getValidDeviceIDList(), message);

        return Response.status(Response.Status.CREATED).entity(activity).build();
    }

    public static int getTenantID(String tenantDomain) throws IOSEnrollmentException {
        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();

            RealmService realmService = (RealmService) ctx.getOSGiService(RealmService.class, null);
            if (realmService == null) {
                String msg = "RealmService is not initialized";
                log.error(msg);
                throw new IOSEnrollmentException(msg);
            }

            int tenantId;
            if (tenantDomain == null || tenantDomain.isEmpty()) {
                tenantId = MultitenantConstants.SUPER_TENANT_ID;
                ctx.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
            } else {
                tenantId = realmService.getTenantManager().getTenantId(tenantDomain);
                ctx.setTenantDomain(tenantDomain);
            }

            if (tenantId == MultitenantConstants.INVALID_TENANT_ID) {
                tenantId = MultitenantConstants.SUPER_TENANT_ID;
            }

            return tenantId;
        } catch (UserStoreException e) {
            String msg = "User store not initialized";
            log.error(msg);
            throw new IOSEnrollmentException(msg, e);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
    }


    /**
     * Returns a new BadRequestException
     *
     * @param description description of the exception
     * @return a new BadRequestException with the specified details as a response DTO
     */
    public static BadRequestException buildBadRequestException(String description) {
        ErrorResponse errorResponse = getErrorResponse(IOSConstants.
                                                               ErrorMessages.STATUS_BAD_REQUEST_MESSAGE_DEFAULT,400l, description);
        return new BadRequestException(errorResponse);
    }

    /**
     * Returns generic ErrorResponse.
     * @param message specific error message
     * @param code
     * @param description
     * @return generic Response with error specific details.
     */
    public static ErrorResponse getErrorResponse(String message, Long code, String description) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode(code);
        errorResponse.setMoreInfo("");
        errorResponse.setMessage(message);
        errorResponse.setDescription(description);
        return errorResponse;
    }

    public static <T> ErrorResponse getConstraintViolationErrorDTO(Set<ConstraintViolation<T>> violations) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setDescription("Validation Error");
        errorResponse.setMessage("Bad Request");
        errorResponse.setCode(400l);
        errorResponse.setMoreInfo("");
        List<ErrorListItem> errorListItems = new ArrayList<>();
        for (ConstraintViolation violation : violations) {
            ErrorListItem errorListItemDTO = new ErrorListItem();
            errorListItemDTO.setCode(400 + "_" + violation.getPropertyPath());
            errorListItemDTO.setMessage(violation.getPropertyPath() + ": " + violation.getMessage());
            errorListItems.add(errorListItemDTO);
        }
        errorResponse.setErrorItems(errorListItems);
        return errorResponse;
    }

    public static void updateDeviceLocation(DeviceLocation deviceLocation) throws DeviceDetailsMgtException {
        PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
        DeviceInformationManager informationManager =
                (DeviceInformationManager) ctx.getOSGiService(DeviceInformationManager.class, null);

        informationManager.addDeviceLocation(deviceLocation);
    }
}
