<?php

namespace Velis;

use DOMDocument;
use Exception;
use RobRichards\XMLSecLibs\XMLSecEnc;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use Velis\Model\BaseModel;

/**
 * Electronic signature base model
 * @author Michał Nosek <michal.nosek@velis.pl>
 */
abstract class Signature extends BaseModel
{
    const TYPE_ENVELOPED = 'ENVELOPED';

    /**
     * Signature xml
     * @var string
     */
    protected $_signature;

    protected $_xml;


    /**
     * @param $document
     * @param string $signature
     */
    public function __construct($document = null, $signature = null)
    {
        $this->_signature = $signature;
    }


    /**
     * Check order sign possibility
     * @return bool
     * @throws Exception when user has no priv to confirm order
     */
    protected function _checkConfirmationPosibility()
    {
        if (!App::$user->signature_hash) {
            throw new Exception('Nie masz przypisanego certyfikatu');
        }

        return true;
    }


    /**
     * @param string $certificate
     * @return bool
     */
    protected function _validateUserToCertificate($certificate)
    {
        if ($certificate != App::$user->signature_hash) {
            return false;
        } else {
            return true;
        }
    }


    /**
     * Validates signature
     * @return bool|void
     * @throws Exception
     */
    public function validateSignature()
    {
        switch ($this->getType()) {
            case self::TYPE_ENVELOPED:
                return self::_validateEnveloped();
        }
    }


    /**
     * Validates enveloped signature
     * @return bool
     * @throws Exception
     */
    protected function _validateEnveloped()
    {
        try {
            if (!$this->_signature) {
                throw new Exception('Brak podpisu');
            }

            $doc = new DOMDocument();

            $doc->loadXML($this->_signature);
            $objXMLSecDSig = new XMLSecurityDSig();

            $objDSig = $objXMLSecDSig->locateSignature($doc);
            if (!$objDSig) {
                throw new Exception("Nie można zlokalizować podpisu w dokumencie");
            }

            $objXMLSecDSig->canonicalizeSignedInfo();
            $objXMLSecDSig->idKeys = array('wsu:Id');
            $objXMLSecDSig->idNS = array('wsu' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd');

            $retVal = $objXMLSecDSig->validateReference();

            if (! $retVal) {
                throw new Exception("Błąd referencji");
            }

            $objKey = $objXMLSecDSig->locateKey();
            if (!$objKey) {
                throw new Exception("Nie odnaleziono klucza w dokumencie");
            }
            $key = null;

            $objKeyInfo = XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig);

            if ($objXMLSecDSig->verify($objKey)) {
                return true;
            } else {
                throw new Exception('Podpis jest nieprawidłowy');
            }
        } catch (Exception $e) {
            throw new Exception('Wystąpił błąd podczas walidacji podpisu: ' . $e->getMessage());
        }
    }


    /**
     * Returns signature as string
     * @return string Signature xml
     */
    public function getSignature()
    {
        return $this->_signature;
    }


    /**
     * Should return signature type
     * @return string
     */
    abstract public function getType();


    /**
     * Should return signature filename
     * @return string
     */
    abstract public function getFilename();

    /**
     * Should return url to redirect after sign applet close
     * @return string
     */
    abstract public function getRedirectUrl();


    /**
     * Should return certificate string
     * @return string
     */
    abstract public function getCertificate();

    abstract public function generateConfirmXml();
}
