/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 1999-2025 CardContact Systems GmbH
 * |'##> <##'|  32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 *  This file is part of OpenSCDP.
 *
 *  OpenSCDP is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  OpenSCDP is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenSCDP; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @fileoverview Common service request controller for backend service requests
 */

var CommonUI = require('scsh/srv-cc1/CommonUI').CommonUI;
var SubjectDAO = require('scsh/pki-db/db/SubjectDAO').SubjectDAO;
var ServiceRequestModel = require('pki-as-a-service/service/ServiceRequestModel').ServiceRequestModel;
var PKIAPIConnector = require('scsh/srv-cc1/PKIAPIConnector').PKIAPIConnector;
var PKIAPICardActionWizard = require('pki-as-a-service/ui/PKIAPICardActionWizard').PKIAPICardActionWizard;
var CVC = require("scsh/eac/CVC").CVC;



function BackendServiceRequestControllerBase(plugin) {
    this.plugin = plugin;
}

exports.BackendServiceRequestControllerBase = BackendServiceRequestControllerBase;


BackendServiceRequestControllerBase.prototype.getTrustCenterByFor = function(req, res, srct) {
	var parameter = CommonUI.parseQueryString(req.queryString);
	var tc = parseInt(parameter.for);

	if (isNaN(tc) || tc == 0) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument 'for' not valid in URL");
	}

	var subject = srct.service.getSubject(tc);

	if ((subject.getType() != SubjectDAO.TYPE_TRUST_CENTER) &&
        (subject.getType() != SubjectDAO.TYPE_DEVICE_ISSUER) &&
        (subject.getType() != SubjectDAO.SubjectDAO.TYPE_SCHEME_ROOT_CA)) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Subject is not a TrustCenter instance");
	}

	return subject;
}



/**
 * Allow derived class to filter candidate from final list.
 */
BackendServiceRequestControllerBase.prototype.isHolderCandidate = function(srct, holder) {
	return true;
}



/**
 * Compile a list of holders of a given type that the user has access to.
 */
BackendServiceRequestControllerBase.prototype.getHolderList = function(srct, type, filter) {
	var holderDAO = srct.ui.service.daof.getHolderDAO();
	var holderList = holderDAO.getHolderListByType(type);

	var list = [];

	for each (var holder in holderList) {
		if (holder.subjectId && holder.signerNo) {
			var tc = srct.service.getSubject(holder.subjectId);
			if (tc.canRead(srct.ui.session.user) || tc.isManagerOf(srct.ui.session.user)) {
				holder.subjectName = tc.getName();
				if (this.isHolderCandidate(srct, holder)) {
					list.push( holder );
				}
			}
		}
	}

	return list;
}




BackendServiceRequestControllerBase.prototype.getHolderSelect = function(srct, type) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var select = <select name={ type.toLowerCase() }/>;

	var holderList = this.getHolderList(srct, type);
	if (holderList.length == 0) {
		return;
	}

	holderList.sort(function(a, b) { return a.subjectId == b.subjectId ? 0 : a.subjectId > b.subjectId ? 1 : -1 });

	var sid = -1;
	var optgroup;
	for each (var holder in holderList) {
		if (sid != holder.subjectId) {
			if (sid != -1) {
				select.appendChild(optgroup);
			}
			optgroup = <optgroup label={ holder.subjectName } />;
			sid = holder.subjectId;
		}

		var opt = <option value={holder.id}>{holder.name}</option>;
		optgroup.appendChild(opt);
	}
	select.appendChild(optgroup);

	return select;
}



BackendServiceRequestControllerBase.prototype.validateHolder = function(srct, type, parameter) {
	var issuer = parseInt(parameter[type.toLowerCase()]);
	if (isNaN(issuer) || issuer <= 0) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument '" + type.toLowerCase() + "' not valid in URL");
	}

	var holderList = this.getHolderList(srct, type);
	for each (var holder in holderList) {
		if (holder.id == issuer) {
			return holder.subjectId;
		}
	}

	throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument '" + type.toLowerCase() + "' does not encode a valid issuer");
}



BackendServiceRequestControllerBase.prototype.postCreateActionViaAPI = function(req, res, srct, data, api) {
	if (api == undefined) {
		api = this.plugin.getAPI();
	}

	try	{
		var rsp = api.postJSON("/sr/", data, srct.ui.session.user.id);

		if (rsp.responseCode != HttpResponse.SC_CREATED) {
			srct.ui.setErrorMessage(rsp.message, rsp.detail);
			return false;
		}

		srct.ui.redirectTo(req, res, req.url, "/sr/" + rsp.srId);
	}
	catch(e) {
		srct.ui.setErrorMessage("api_failed", "Connection to backend failed with : " + e.message);
		return false;
	}
	return true;
}



BackendServiceRequestControllerBase.prototype.postProcessAction = function(req, res, srct, sr, action) {

	if (action == undefined) {
		action = "action.save";
	}

	var context = "";
	var redirect = false;

	var i = action.indexOf("@");
	if (i > 0) {
		if (this.postProcessAction(req, res, srct, sr, "action.save")) {
			return true;
		}

		context = action.substr(i + 1);
		action = action.substr(0, i);
		var desc = srct.service.backendRegistry.getService(context);
		var api = new PKIAPIConnector(desc);
	} else {
		var api = this.plugin.getAPI();
	}

	var data = { action: action, args: req.parameters };
	try {
		var result = api.postAction(sr.getId(), data, srct.ui.session.user.id);

		if (result.message == PKIAPIConnector.COMPLETED) {
			// Check if role assigned changed and send notification
			var nsr = srct.service.getServiceRequestById(sr.getId());
			if (sr.bo.assignedToRole != nsr.bo.assignedToRole) {
				nsr.notifyRoleOwner(srct.ui.session.user.id);
			}
		}
	}
	catch(e) {
		srct.ui.setErrorMessage("failed", e.message);
		return false;
	}

	if (result.message == PKIAPIConnector.FAILED) {
		sr.setServiceResponse(result.sr);
		srct.ui.setErrorMessage("failed", result.detail);
		var page = srct.getProcessView(req, res, sr);
		srct.ui.sendPage(req, res, req.url, page);
		return true;
	}

	if (result.message == PKIAPIConnector.PENDING) {
		var data = { user: srct.ui.session.user.id, action: action, args: req.parameter };
		var redirectUrl = "/" + req.url[1] + "/" + req.url[2];
		srct.ui.wizard = new PKIAPICardActionWizard(srct.ui, api, sr.getId(), data, this.plugin.cfg.rtURL, redirectUrl);
		srct.ui.wizard.sessionId = result.sessionId;
		if (this.aid) {
			srct.ui.wizard.aid = this.aid;
		}
		srct.ui.redirectTo(req, res, req.url, "/wizard");
		return true;
	}

	return false;
}



BackendServiceRequestControllerBase.prototype.getProcessView = function(req, res, srct, sr) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var div = <div />;

	if (typeof(sr.sr) == "undefined") {
		var api = this.plugin.getAPI();
		try	{
			var json = api.getServiceRequest(sr.getId(), srct.ui.session.user.id, true);
		}
		catch(e) {
			srct.ui.setErrorMessage("api_failed", "Connection to backend failed with : " + e.message);
			return div;
		}

		sr.setServiceResponse(json);
	}

	div.appendChild(srct.renderForm(req, res, sr));

	if (sr.getLifeCycle() > ServiceRequestModel.LIFECYCLE_PRODUCE) {
		if (this.withCertificate) {
			var certDAO = srct.service.daof.getCertificateDAO();
			var certs = certDAO.enumerateCertificatesByServiceRequestId(sr.getId());
			if (certs.length > 0) {
				div.appendChild( <br/> );
				div.appendChild( <h2>Certificate</h2> );
				for (var i = 0; i < certs.length; i++) {
					var cvc = new CVC(certs[i].bytes);
					div.appendChild( <p><a href={ "../cert/" + certs[i].id }>{cvc.toString() }</a></p>);
				}
			}
		} else if (this.withX509Certificate) {
			div.appendChild( <br/> );
			div.appendChild( <h2>Certificates</h2> );
			div.appendChild(srct.renderCertificateList(req, res, sr));
		}
	}

	return div;
}
