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

import org.apache.commons.lang.StringUtils;
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.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.app.mgt.Application;
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.core.device.details.mgt.DeviceDetailsMgtException;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.APNSBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.DeviceIdentifierBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.LocationBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.TokenBean;
import org.wso2.carbon.device.mgt.ios.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.ios.core.bean.ChallengeTokenBean;
import org.wso2.carbon.device.mgt.ios.core.exception.IOSEnrollmentException;
import org.wso2.carbon.device.mgt.ios.core.publisher.TokenPersistence;
import org.wso2.carbon.device.mgt.ios.exception.BadRequestException;
import org.wso2.carbon.device.mgt.ios.exception.NotFoundException;
import org.wso2.carbon.device.mgt.ios.exception.UnexpectedServerErrorException;
import org.wso2.carbon.device.mgt.ios.services.DeviceManagementService;
import org.wso2.carbon.device.mgt.ios.util.DeviceUtils;
import org.wso2.carbon.device.mgt.ios.util.EnrollmentUtils;
import org.wso2.carbon.device.mgt.ios.util.IOSServiceUtils;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import org.wso2.carbon.policy.mgt.core.PolicyManagerService;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Date;
import java.util.List;

@Path("/devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class DeviceManagementServiceImpl implements DeviceManagementService {

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

    @GET
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/{id}")
    public Response getDeviceInformation(@HeaderParam("Accept") String acceptHeader,
                                         @PathParam("id") String deviceId) {
        String msg;
        if (log.isDebugEnabled()) {
            log.debug("Requesting device information from " + deviceId);
        }

        MediaType responseMediaType = IOSServiceUtils.getResponseMediaType(acceptHeader);
        DeviceUtils deviceUtils = new DeviceUtils();
        DeviceIdentifier deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(deviceId);
        try {
            Device device = IOSServiceUtils.getDeviceManagementService().getDevice(deviceIdentifier);

            if (device == null || device.getDeviceIdentifier() == null || device.getDeviceIdentifier().isEmpty()) {
                msg = "Device not found for requested device id";
                throw new NotFoundException(
                        new ErrorResponse.ErrorResponseBuilder().setCode(404l).setMessage(msg).build());
            } else {
                return Response.status(Response.Status.OK).entity(device).type(responseMediaType).build();
            }
        } catch (DeviceManagementException e) {
            msg = "Error occurred when retrieving device information";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        }
    }

    @PUT
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/pushtoken/{id}")
    public Response
    updateAPNSToken(@HeaderParam("Accept") String acceptHeader,
                                    @PathParam("id") String deviceId,
                                    APNSBean apnsBean) {

        String msg;
        if (log.isDebugEnabled()) {
            log.debug("Updating push token of device: " + deviceId);
        }

        MediaType responseMediaType = IOSServiceUtils.getResponseMediaType(acceptHeader);
        DeviceUtils deviceUtils = new DeviceUtils();
        DeviceIdentifier deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(deviceId);

        try {
            Device device = IOSServiceUtils.getDeviceManagementService().getDevice(deviceIdentifier);

            if (device == null || device.getDeviceIdentifier() == null || device.getDeviceIdentifier().isEmpty()) {
                msg = "Device not found for requested device id";
                throw new NotFoundException(
                        new ErrorResponse.ErrorResponseBuilder().setCode(404l).setMessage(msg).build());
            } else {
                TokenPersistence tokenPersistence = new TokenPersistence();
                tokenPersistence.saveAPNSToken(deviceIdentifier, apnsBean.getToken());

                return Response.status(Response.Status.OK).entity("Pushtoken stored successfully").type
                        (responseMediaType).build();
            }
        } catch (DeviceManagementException e) {
            msg = "Error occurred while updating push token";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        }
    }

    @PUT
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/location/{id}")
    public Response updateLocation(@HeaderParam("Accept") String acceptHeader,
                                   @PathParam("id") String deviceId,
                                   LocationBean locationBean) {
        if (log.isDebugEnabled()) {
            log.debug("Updating device location of device: " + deviceId);
        }

        MediaType responseMediaType = IOSServiceUtils.getResponseMediaType(acceptHeader);
        DeviceUtils deviceUtils = new DeviceUtils();
        DeviceIdentifier deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(deviceId);

        try {
            Device device = IOSServiceUtils.getDeviceManagementService().getDevice(deviceIdentifier);

            if (device == null || device.getDeviceIdentifier() == null || device.getDeviceIdentifier().isEmpty()) {
                throw new NotFoundException(
                        new ErrorResponse.ErrorResponseBuilder().setCode(404l).
                                setMessage("Device not found for requested device id").build());
            } else {
                TokenPersistence tokenPersistence = new TokenPersistence();
                tokenPersistence.saveDeviceLocation(deviceIdentifier,
                        locationBean.getLatitude(), locationBean.getLongitude() ,
                        locationBean.getOperationId());
                DeviceLocation location = new DeviceLocation();
                location.setDeviceIdentifier(deviceIdentifier);
                location.setLongitude((double) locationBean.getLongitude());
                location.setLatitude((double) locationBean.getLatitude());
                location.setStreet1("");
                location.setStreet2("");
                location.setCity("");
                location.setZip("");
                location.setState("");
                location.setCountry("");
                Date date = new Date();
                location.setUpdatedTime(date);
                IOSServiceUtils.updateDeviceLocation(location);
                return Response.status(Response.Status.OK).entity("Location has been updated").type(responseMediaType)
                        .build();
            }
        } catch (DeviceManagementException e) {
            String msg = "Error occurred when storing device location";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        } catch (DeviceDetailsMgtException e) {
            String msg = "Error occurred when storing device location";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        }
    }

    @POST
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/udid")
    public Response getDeviceUDID(@HeaderParam("Accept") String acceptHeader,
                                  TokenBean tokenBean) {

        if (log.isDebugEnabled()) {
            log.debug("Requesting device identifier for token");
        }

        MediaType responseMediaType = IOSServiceUtils.getResponseMediaType(acceptHeader);
        DeviceIdentifierBean deviceIdentifierBean = new DeviceIdentifierBean();
        try {
            ChallengeTokenBean challengeTokenBean = EnrollmentUtils.getTokenEntry(tokenBean.getChallengeToken());
            if (challengeTokenBean != null) {

                String username = challengeTokenBean.getUsername();
                String tenantDomain = challengeTokenBean.getDomain();

                int tenantId = MultitenantConstants.SUPER_TENANT_ID;

                if(tenantDomain != null && !tenantDomain.isEmpty()) {
                    tenantId = IOSServiceUtils.getTenantID(tenantDomain);
                }

                if(username != null) {
                    PrivilegedCarbonContext.startTenantFlow();
                    PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext.
                            getThreadLocalCarbonContext();
                    if(tenantDomain != null && !tenantDomain.isEmpty()) {
                        privilegedCarbonContext.setTenantDomain(tenantDomain);
                    } else {
                        privilegedCarbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
                    }
                    privilegedCarbonContext.setUsername(username);
                    privilegedCarbonContext.setTenantId(tenantId);
                }

                challengeTokenBean = EnrollmentUtils.getTokenEntry(tokenBean.getChallengeToken());
                if (challengeTokenBean != null  && !StringUtils.isEmpty(challengeTokenBean.getDeviceID())) {
                    deviceIdentifierBean.setDeviceID(challengeTokenBean.getDeviceID());
                    deviceIdentifierBean.setRefreshToken(challengeTokenBean.getRefreshToken());
                    deviceIdentifierBean.setAccessToken(challengeTokenBean.getAccessToken());
                    deviceIdentifierBean.setClientCredentials(challengeTokenBean.getClient());
                    if (log.isDebugEnabled()) {
                        log.debug(
                                "Removing enrollment token entry for device id: " + deviceIdentifierBean.getDeviceID());
                    }
                    //EnrollmentUtils.removeTokenEntry(deviceIdentifierBean.getDeviceID());
                }
            }
        } catch (IOSEnrollmentException e) {
            String errorMessage = "Invalid token passed to retrieve the device UDID";
            throw new BadRequestException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
        return Response.status(Response.Status.OK).entity(deviceIdentifierBean).type(responseMediaType).build();
    }

    @GET
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/{id}/applications")
    public Response getApplicationList(@PathParam("id") String deviceId) {

        if (log.isDebugEnabled()) {
            log.debug("Requesting applications list from " + deviceId);
        }

        DeviceUtils deviceUtils = new DeviceUtils();
        DeviceIdentifier deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(deviceId);

        try {
            List<Application> applicationList = IOSServiceUtils.getApplicationManagementService()
                    .getApplicationListForDevice(deviceIdentifier);

            return Response.status(Response.Status.OK).entity(applicationList).build();
        } catch (ApplicationManagementException e) {
            String msg = "Error occurred when retrieving applications";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        }
    }

    @GET
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/effective-policy/{id}")
    public Response getEffectivePolicy(@PathParam("id") String deviceId) {

        if (log.isDebugEnabled()) {
            log.debug("Requesting effective policy of " + deviceId);
        }
        DeviceUtils deviceUtils = new DeviceUtils();
        DeviceIdentifier deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(deviceId);

        try {
            PolicyManagerService policyManagerService = IOSServiceUtils.getPolicyManagerService();
            policyManagerService.getEffectivePolicy(deviceIdentifier);
            return Response.status(Response.Status.OK).entity("Effective policy is enforced").build();
        } catch (PolicyManagementException e) {
            String msg = "Error occurred while enforcing effective policy";
            log.error(msg, e);
            throw new UnexpectedServerErrorException(
                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(msg).build());
        }
    }
}