package in.gov.digilocker.uidai.services.client;

import java.security.KeyStore;
import java.util.Base64;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import in.gov.digilocker.uidai.logs.modal.DemoAuth;
import in.gov.digilocker.uidai.services.docsigner.DocSigner;
import in.gov.digilocker.uidai.services.properties.DemoAuthProperties;
import in.gov.digilocker.uidai.services.properties.UIDAIProps;
import in.gov.digilocker.uidai.services.properties.UidaiErrors;
import in.gov.digilocker.uidai.services.resources.DemoAuthPIDBlock;
import in.gov.digilocker.uidai.services.resources.PostData;
import in.gov.digilocker.uidai.services.resources.RDEncrypter;
import in.gov.digilocker.uidai.services.resources.Utilities;
import in.gov.digilocker.uidai.services.xmlresponse.AuthResponse;
import in.gov.digilocker.uidai.web.properties.DigilockerProperties;

@Service
public class DemoAuthClient {

	private static final Logger logger = LoggerFactory.getLogger(DemoAuthClient.class);
	@Autowired UIDAIProps uidaiProps;
	@Autowired DigilockerProperties digilockerProps;
	@Autowired DemoAuthProperties demoAuthProperties;
	@Autowired UidaiErrors uidaiErrors;

	public AuthResponse validateDetailsUsingDemographicDetails(DemoAuth demoAuth, String reqTimeStamp) throws RuntimeException, Exception {
		logger.info("Demographical authentication 2.5 process starts.");
		try {
			String contentType = "application/xml";
			RDEncrypter enc = new RDEncrypter(uidaiProps.getUidaiCertPath());
			KeyStore.PrivateKeyEntry keyEntry = enc.getKeyFromKeyStore(digilockerProps.getDigilockerCertPath(), digilockerProps.getDigilockerCertPassword(), digilockerProps.getDigilockerCertAlias());
			if (keyEntry == null) {
				throw new RuntimeException("ERR-107, KeyFinder.getKeyFromKeyStore has returned null value.");
			}
			DemoAuthPIDBlock pidBlock = new DemoAuthPIDBlock();
			pidBlock.setTs(reqTimeStamp);
			pidBlock.setPidVer(uidaiProps.getUidaiPIDVer());
			pidBlock.setMs(demoAuthProperties.getMatchSchemaName());
			pidBlock.setMv(demoAuthProperties.getMatchSchemaVal());
			pidBlock.setName(demoAuth.getFullname());
			pidBlock.setGender(demoAuth.getGender());
			pidBlock.setDob(demoAuth.getDob());
			pidBlock.setPhone(demoAuth.getPhone());
			pidBlock.setPc(demoAuth.getPin());
			
			String pidxml = pidBlock.generatePIDXMLForDemoAuth();

			byte[] sessionKey = enc.generateSessionKey();
			byte[] encryptedSessionKey = enc.encryptUsingPublicKey(sessionKey);

			byte[] pidXmlBytes = pidxml.getBytes(uidaiProps.getEncodingType());
			byte[] encXMLPIDData = enc.encrypt(pidXmlBytes, sessionKey, reqTimeStamp);

			byte[] iv = enc.generateIv(reqTimeStamp);
			byte[] aad = enc.generateAad(reqTimeStamp);
			byte[] hmac = enc.generateHash(pidXmlBytes);
			byte[] encryptedHmacBytes = enc.encryptDecryptUsingSessionKey(true, sessionKey, iv, aad, hmac);

			String certificate_expiry = enc.getCertificateIdentifier();
			
			String demoAuthXML = new DemoAuthClient().createDemoAuthRequest(uidaiProps, demoAuthProperties, demoAuth.getAadhaarNumber(), demoAuth.getUniqueReqId(), demoAuth.getConsent(), demoAuth.getPin(), demoAuth.getFullname(), certificate_expiry, encryptedSessionKey, encryptedHmacBytes, encXMLPIDData);
			
			String signAuthXML = DocSigner.signXML(demoAuthXML, true, keyEntry);
			
			Utilities.checkProxy(digilockerProps.isProxy(), digilockerProps.getProxyHost(), digilockerProps.getProxyPort());
		    
			String respXML = new PostData().excutePostXml(uidaiProps.getUidaiServiceUrl(), signAuthXML, contentType);
		    
		    AuthResponse authResponse = (respXML != null) && (!"".equals(respXML)) ? AuthResponse.buildAuthResponse(respXML) : new AuthResponse();
		    
		    if ("y".equalsIgnoreCase(authResponse.getRet())) {
		    	logger.info("User authentication process using demographic details is completed successfully, for more details please see the log.");
		    } else {
		    	for (Entry<String, String> entry : uidaiErrors.getErrorMap().entrySet()) {
		            if (entry.getKey().trim().equals(authResponse.getErr().trim())) {
		            	authResponse.setMsg(entry.getValue().trim());		            				                
		            }
		        }
		    	logger.info("Authentication using demographic details is failed due to " + authResponse.getMsg() + ", for more details please see the log.");
		    }
		    authResponse.setXmlResp(respXML);
		    return authResponse;
		} catch (RuntimeException e) {
			logger.info("Runtime exception in DemoAuthClient.validateDetailsUsingDemographicDetails:- " +e.getMessage());
			throw e;
		} catch (Exception e) {
			logger.info("Exception in DemoAuthClient.validateDetailsUsingDemographicDetails:- " +e.getMessage());
			throw e;
		}
	}

	private String createDemoAuthRequest(UIDAIProps uidaiProps1, DemoAuthProperties demoAuthProperties1,  String uid, String unique_req_id, String rc, String pincode, String name, String certificate_expiry,  byte[] encryptedSessionKey, byte[] encryptedHmacBytes, byte[] encXMLPIDData) throws Exception {
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

		Document document = docBuilder.newDocument();
		Element rootElement = document.createElement("Auth");

		Attr uidAttr = document.createAttribute("uid");
		uidAttr.setValue(uid.trim());
		rootElement.setAttributeNode(uidAttr);

		Attr rcAttr = document.createAttribute("rc");
		rcAttr.setValue(rc.trim());
		rootElement.setAttributeNode(rcAttr);

		Attr tidAttr = document.createAttribute("tid");
		tidAttr.setValue("");
		rootElement.setAttributeNode(tidAttr);

		Attr acAttr = document.createAttribute("ac");
		acAttr.setValue(demoAuthProperties1.getAc());
		rootElement.setAttributeNode(acAttr);

		Attr saAttr = document.createAttribute("sa");
		saAttr.setValue(demoAuthProperties1.getSa());
		rootElement.setAttributeNode(saAttr);

		Attr verAttr = document.createAttribute("ver");
		verAttr.setValue(demoAuthProperties1.getDemoAuthVersion());
		rootElement.setAttributeNode(verAttr);

		Attr txnAttr = document.createAttribute("txn");
		txnAttr.setValue(unique_req_id);
		rootElement.setAttributeNode(txnAttr);

		Attr lkAttr = document.createAttribute("lk");
		lkAttr.setValue(demoAuthProperties1.getLk());
		rootElement.setAttributeNode(lkAttr);

		Element usesElement = document.createElement("Uses");
		usesElement.setAttribute("pi", !"".equals(name) ? "y" : "n");
		usesElement.setAttribute("pa", !"".equals(pincode) ? "y" : "n");
		usesElement.setAttribute("bt", "");
		usesElement.setAttribute("pfa", "n");
		usesElement.setAttribute("bio", "n");
		usesElement.setAttribute("pin", "n");
		usesElement.setAttribute("otp", "n");
		rootElement.appendChild(usesElement);

		Element metaElement = document.createElement("Meta");
		metaElement.setAttribute("udc", demoAuthProperties1.getUdc());
		metaElement.setAttribute("rdsId", "");
		metaElement.setAttribute("rdsVer", "");
		metaElement.setAttribute("dpId", "");
		metaElement.setAttribute("dc", "");
		metaElement.setAttribute("mi", "");
		metaElement.setAttribute("mc", "");
		rootElement.appendChild(metaElement);

		Element skeyElement = document.createElement("Skey");
		skeyElement.setAttribute("ci", certificate_expiry);
		skeyElement.setTextContent(Base64.getEncoder().encodeToString(encryptedSessionKey));
		rootElement.appendChild(skeyElement);

		Element hmacElement = document.createElement("Hmac");
		hmacElement.setTextContent(Base64.getEncoder().encodeToString(encryptedHmacBytes));
		rootElement.appendChild(hmacElement);

		Element dataElement = document.createElement("Data");
		dataElement.setAttribute("type", "X");
		dataElement.setTextContent(Base64.getEncoder().encodeToString(encXMLPIDData));
		rootElement.appendChild(dataElement);

		document.appendChild(rootElement);

		String otpAuthXML = Utilities.convertDocumentToString(document);

		return otpAuthXML;
	}

}
