package in.gov.digilocker.uidai.web.controller.v1;

import java.net.Inet4Address;
import java.util.Base64;
import java.util.Date;

import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import in.gov.digilocker.uidai.logs.constants.OTPAuthCollection;
import in.gov.digilocker.uidai.logs.fpmodal.BiomatricData;
import in.gov.digilocker.uidai.logs.modal.OTPAuth;
import in.gov.digilocker.uidai.logs.sender.MessageSendTaskExecutor;
import in.gov.digilocker.uidai.services.client.AuthClient;
import in.gov.digilocker.uidai.services.properties.OTPAuthProperties;
import in.gov.digilocker.uidai.services.properties.UIDAIProps;
import in.gov.digilocker.uidai.services.properties.UidaiErrors;
import in.gov.digilocker.uidai.services.xmlresponse.AuthResponse;
import in.gov.digilocker.uidai.web.exceptions.KeyMismatchException;
import in.gov.digilocker.uidai.web.properties.DigilockerProperties;
import in.gov.digilocker.uidai.web.validator.Validator;

@RestController
@RequestMapping(value = "/api")
public class AuthController1 {

	private static final Logger logger = LoggerFactory.getLogger(AuthController1.class);

	@Autowired Validator validator;
	@Autowired DigilockerProperties properties;
	@Autowired UidaiErrors uidaiErrors;
	@Autowired MessageSendTaskExecutor taskExecutor;
	@Autowired UIDAIProps uidaiProps;
	@Autowired AuthClient authClient;
	@Autowired OTPAuthProperties authProperties;

	@SuppressWarnings("deprecation")
	@PostMapping(value = "/getauth", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	public ResponseEntity<String> postAuth(
			@RequestParam(name = "transaction_id", required = false) String uniquereqid,
			@RequestParam(name = "AadhaarNumber", required = true) String uid,
			@RequestParam(name = "type", required = false, defaultValue = "A") String type,
			@RequestParam(name = "OTP", required = true) String otp,
			@RequestParam(name = "fpenabled", required = false, defaultValue = "N") String fpEnabled,
			@RequestParam(name = "authtype", required = false, defaultValue = "auth") String authtype,
			@RequestParam(name = "consent", required = true, defaultValue = "N") String consent,
			@RequestParam(name = "aadhaar_token", required = true) String token,
			@RequestParam(name = "key", required = false, defaultValue = "TEST") String clientkey) {

		
		OTPAuth otpAuth = new OTPAuth();				
				
		
		String[] infoArray = null;
		String uidToken = "";
		String reqTimeStamp = "";
		double reqTime = 0.0D;
		double resTime = 0.0D;
		String serverIP = "";
		
		try {
			serverIP = Inet4Address.getLocalHost().getHostAddress();
			reqTimeStamp = validator.getCurrentDateTimeISOFormat();
			validator.validateToken(token);
			validator.validateUID(uid, type);
			validator.validateOtp(otp, fpEnabled);
			uniquereqid = validator.generateTransactionId(uniquereqid, uid, authtype);
			authtype = validator.setAuthTypeForOTPAuthentication(authtype);
			consent = validator.setResidentialConsent(consent);
			String localApiKey = validator.createAPIKey(properties.getApiKeyDateFormat(), token);
			if (properties.isExceptionEnabled()) {
				logger.info("API Keys is:- " + localApiKey);
			}
			clientkey = "TEST";
			if (!"TEST".equals(clientkey)) {
				throw new KeyMismatchException("Invalid client key.");
			}
			
			otpAuth.setUniqueReqId(uniquereqid);
			otpAuth.setAadhaarNumber(uid);
			otpAuth.setType(type);			
			otpAuth.setFpEnabled(fpEnabled);
			otpAuth.setAuthType(authtype);
			otpAuth.setConsent(consent);
			otpAuth.setServerIP(serverIP);

			reqTime = System.currentTimeMillis();
			AuthResponse authResponse;			
			if("Y".equalsIgnoreCase(fpEnabled)) {
				String decodeOtp= new String(Base64.getDecoder().decode(otp.replaceAll("\\Q**\\E", "+")));
				otpAuth.setOtp(decodeOtp);
				BiomatricData biomatricResponse = BiomatricData.buildXMLToBioMatricData(otpAuth.getOtp());
				authResponse = authClient.verifyAuthenticationUsingFingerPrint(uidaiProps, properties, authProperties, uidaiErrors, otpAuth, reqTimeStamp, biomatricResponse);
			} else {
				otpAuth.setOtp(otp);
				authResponse = authClient.verifyAuthenticationUsingOTP(uidaiProps, properties, authProperties, uidaiErrors, otpAuth, reqTimeStamp);
			}
			
			resTime = System.currentTimeMillis();

			infoArray = (null != authResponse) && (!"".equalsIgnoreCase(authResponse.getInfo())) ? authResponse.getInfo().split(",") : null;
			uidToken = (null != infoArray) ? infoArray[0].substring(3, infoArray[0].length()).trim() : "";
			otpAuth.setUidToken(uidToken);
			otpAuth.setAvgResTime((resTime - reqTime) / 1000D);
			boolean status = false;
			String msg = "";
			String code="";			
			JSONObject jsonObject = new JSONObject();
			
			if ("y".equalsIgnoreCase(authResponse.getRet())) {
				msg = "Your Authentication is Successful.";
				status = true;
				otpAuth.setStatus(status);
				otpAuth.setResponse("");
				otpAuth.setErrCode(authResponse.getErr());
				otpAuth.setErrMsg(msg);
				jsonObject.put("code", authResponse.getErr());
				jsonObject.put("uid_token", uidToken);
				jsonObject.put("status", otpAuth.isStatus());
				jsonObject.put("access_token", token);
				jsonObject.put("msg", msg);
				jsonObject.put("transaction_id", otpAuth.getUniqueReqId());
				
				if (this.properties.isMsgqueueEnabled()) {
					taskExecutor.executeTask(otpAuth.toJSONObject(), "C", OTPAuthCollection.OTP_AUTH_COLLECTION_NAME, OTPAuthCollection.OTP_AUTH_DATABASE_NAME); 
				}
				
				return new ResponseEntity<String>(jsonObject.toString(), HttpStatus.OK);
			}
			
			if (code.equals("1200") || code.equals("1201") || code.equals("1202") || code.equals("1203") || code.equals("1204") || code.equals("1205")) { 
				code= authResponse.getErr();
				msg = "Some technical error occured. (#"+authResponse.getMsg()+")";
			} else {
				code= "101";
				msg = "Your Authentication Failed.(#" +authResponse.getErr()+")";				
			}
			otpAuth.setStatus(status);
			otpAuth.setResponse(authResponse.getXmlResp());
			otpAuth.setErrCode(code);
			otpAuth.setErrMsg(msg);

			jsonObject.put("status", status);
			jsonObject.put("code", code);
			jsonObject.put("msg", msg);
			jsonObject.put("access_token", token);
			jsonObject.put("uid_token", uidToken);
			jsonObject.put("transaction_id", otpAuth.getUniqueReqId());
			
			if (this.properties.isMsgqueueEnabled()) {
				taskExecutor.executeTask(otpAuth.toJSONObject(), "C", OTPAuthCollection.OTP_AUTH_COLLECTION_NAME, OTPAuthCollection.OTP_AUTH_DATABASE_NAME); 
			}
			
			return new ResponseEntity<String>(jsonObject.toString(), HttpStatus.EXPECTATION_FAILED);
		} catch (RuntimeException e) {
			
			
			
			logger.error("Runtime Exception in auth home:- " + e.getLocalizedMessage());			 
			
			JSONObject jsonObject = new JSONObject();
			jsonObject.put("status", false);
			jsonObject.put("access_token", token);
			jsonObject.put("uid_token", "");
			jsonObject.put("code", "102");
			jsonObject.put("transaction_id", otpAuth.getUniqueReqId());
			jsonObject.put("msg", e.getMessage());
			jsonObject.put("response", "");
			
			otpAuth.setUniqueReqId(uniquereqid);
			otpAuth.setAadhaarNumber(uid);
			otpAuth.setUidToken("");
			otpAuth.setType(type);
			otpAuth.setOtp(otp);
			otpAuth.setAuthType(authtype);
			otpAuth.setConsent(consent);
			otpAuth.setAvgResTime(0.0);
			otpAuth.setStatus(false);
			otpAuth.setResponse("");
			otpAuth.setErrCode("102");
			otpAuth.setErrMsg(e.getLocalizedMessage());
			
			if (this.properties.isMsgqueueEnabled()) {
				taskExecutor.executeTask(otpAuth.toJSONObject(), "C", OTPAuthCollection.OTP_AUTH_COLLECTION_NAME, OTPAuthCollection.OTP_AUTH_DATABASE_NAME); 
			}
			
			return new ResponseEntity<String>(jsonObject.toString(), HttpStatus.METHOD_FAILURE);
		} catch (Exception e) {
			logger.error("Exception in auth home:- " + e.getLocalizedMessage());
			
			JSONObject jsonObject = new JSONObject();
			jsonObject.put("status", false);
			jsonObject.put("access_token", token);
			jsonObject.put("uid_token", "");
			jsonObject.put("code", "102");
			jsonObject.put("transaction_id", otpAuth.getUniqueReqId());
			jsonObject.put("msg", e.getMessage());
			jsonObject.put("response", "");
			
			otpAuth.setUniqueReqId(uniquereqid);
			otpAuth.setUidToken("");
			otpAuth.setAadhaarNumber(uid);
			otpAuth.setType(type);
			otpAuth.setOtp(otp);
			otpAuth.setAuthType(authtype);
			otpAuth.setConsent(consent);
			otpAuth.setAvgResTime(0.0);
			otpAuth.setStatus(false);
			otpAuth.setResponse("");
			otpAuth.setErrCode("102");
			otpAuth.setErrMsg(e.getLocalizedMessage());
			
			if (this.properties.isMsgqueueEnabled()) {
				taskExecutor.executeTask(otpAuth.toJSONObject(), "C", OTPAuthCollection.OTP_AUTH_COLLECTION_NAME, OTPAuthCollection.OTP_AUTH_DATABASE_NAME); 
			}
			
			return new ResponseEntity<String>(jsonObject.toString(), HttpStatus.METHOD_FAILURE);
		}
	}


	@GetMapping(value = "/getauth", produces = MediaType.APPLICATION_JSON_VALUE)
	public String getAuth() {
		JSONObject jsonObject = new JSONObject();
		try {
			jsonObject.put("code", -1);
			jsonObject.put("status", false);
			jsonObject.put("response", "Get method not allowed here.");
			jsonObject.put("date", new Date());
		} catch (JSONException e) {
			logger.error("Exception auth home:- " + e.getLocalizedMessage());
			jsonObject.put("code", -1);
			jsonObject.put("status", false);
			jsonObject.put("response", e.getLocalizedMessage());
			jsonObject.put("date", new Date());
		}
		return jsonObject.toString();
	}
}