/*
 * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.carbon.apimgt.keymgt.issuers;

import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.wso2.carbon.apimgt.api.APIManagementException;
import org.wso2.carbon.apimgt.api.model.Application;
import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.dto.ApplicationDTO;
import org.wso2.carbon.apimgt.impl.dto.JwtTokenInfoDTO;
import org.wso2.carbon.apimgt.impl.factory.KeyManagerHolder;
import org.wso2.carbon.apimgt.impl.utils.APIUtil;
import org.wso2.carbon.apimgt.keymgt.token.APIMJWTGenerator;
import org.wso2.carbon.apimgt.keymgt.util.APIMTokenIssuerUtil;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
import org.wso2.carbon.identity.oauth2.token.OauthTokenIssuerImpl;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;

import java.text.ParseException;
import java.util.Arrays;
import java.util.List;

public class APIMTokenIssuer extends OauthTokenIssuerImpl {

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

    public APIMTokenIssuer() throws IdentityOAuth2Exception {
    }

    @Override
    public String accessToken(OAuthTokenReqMessageContext tokReqMsgCtx) throws OAuthSystemException {

        String clientId = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId();
        try {
            long start_time = 0;
            if (log.isDebugEnabled()) {
                start_time = System.nanoTime();
            }
            Application application = APIUtil.getApplicationByClientId(clientId);
            if (log.isDebugEnabled()) {
                long end_time = System.nanoTime();
                long output = end_time - start_time;
                log.debug("Time taken to load the Application from database in milliseconds : " + output / 1000000);
            }
            if (application != null) {
                String tokenType = application.getTokenType();
                if (APIConstants.JWT.equals(tokenType)) {
                    OAuthAppDO oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId);
                    String[] audience = oAuthAppDO.getAudiences();
                    List<String> audienceList = Arrays.asList(audience);
                    String[] scopes = tokReqMsgCtx.getScope();
                    StringBuilder scopeString = new StringBuilder();
                    for (String scope : scopes) {
                        scopeString.append(scope).append(" ");
                    }

                    ApplicationDTO applicationDTO = new ApplicationDTO();
                    applicationDTO.setId(application.getId());
                    applicationDTO.setName(application.getName());
                    applicationDTO.setTier(application.getTier());

                    JwtTokenInfoDTO jwtTokenInfoDTO = APIMTokenIssuerUtil.getJwtTokenInfoDTO(application);
                    jwtTokenInfoDTO.setScopes(scopeString.toString().trim());
                    jwtTokenInfoDTO.setAudience(audienceList);
                    jwtTokenInfoDTO.setExpirationTime(tokReqMsgCtx.getValidityPeriod());
                    jwtTokenInfoDTO.setApplication(applicationDTO);
                    jwtTokenInfoDTO.setKeyType(application.getKeyType());
                    APIMJWTGenerator apimjwtGenerator = new APIMJWTGenerator();
                    String accessToken = apimjwtGenerator.generateJWT(jwtTokenInfoDTO);
                    if (log.isDebugEnabled()) {
                        long end_time_2 = System.nanoTime();
                        long output = end_time_2 - start_time;
                        log.debug("Time taken to generate the JWG in milliseconds : " + output / 1000000);
                    }
                    return accessToken;
                }
            }

        } catch (APIManagementException e) {
            log.error("Error occurred while getting JWT Token client ID : " + clientId, e);
            throw new OAuthSystemException("Error occurred while getting JWT Token client ID : " + clientId, e);
        } catch (InvalidOAuthClientException e) {
            log.error("Error occurred while getting JWT Token client ID : " + clientId + " when getting oAuth App " +
                    "information", e);
            throw new OAuthSystemException("Error occurred while getting JWT Token client ID : " + clientId, e);
        } catch (IdentityOAuth2Exception e) {
            log.error("Error occurred while getting JWT Token client ID : " + clientId + " when getting oAuth App " +
                    "information", e);
            throw new OAuthSystemException("Error occurred while getting JWT Token client ID : " + clientId, e);
        }
        return super.accessToken(tokReqMsgCtx);
    }

    @Override
    public String getAccessTokenHash(String accessToken) throws OAuthSystemException {
        if (StringUtils.isNotEmpty(accessToken) && accessToken.contains(APIConstants.DOT)) {
            try {
                JWT parse = JWTParser.parse(accessToken);
                return parse.getJWTClaimsSet().getJWTID();
            } catch (ParseException e) {
                if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) {
                    log.debug("Error while getting JWTID from token: " + accessToken);
                }
                throw new OAuthSystemException("Error while getting access token hash", e);
            }
        } else {
            return accessToken;
        }
    }
}

@@ -4293,16 +4293,37 @@
 ionTime(
+getSecondsTillExpiry(
 tokReqMs
@@ -4343,24 +4343,25 @@
 ityPeriod())
+)
 ;%0A          
@@ -5977,32 +5977,710 @@
 MsgCtx);%0A    %7D%0A%0A
+    private long getSecondsTillExpiry(long validityPeriod) throws APIManagementException %7B%0A        if (validityPeriod == -1) %7B%0A            // the token request does not specify the validity period explicitly%0A            KeyManagerConfiguration configuration = KeyManagerHolder.getKeyManagerInstance().getKeyManagerConfiguration();;%0A            return Long.parseLong(configuration.getParameter(APIConstants.IDENTITY_OAUTH2_FIELD_VALIDITY_PERIOD));%0A        %7D else if (validityPeriod == -2) %7B%0A            // a non-expiring token request, set the expiration to a large value%0A            return Integer.MAX_VALUE;%0A        %7D else %7B%0A            return validityPeriod;%0A        %7D%0A    %7D%0A%0A
     @Override%0A  

Revert "Remove JWT default time duration handling temporarily"