"""Functions for creating XML output."""
from __future__ import annotations

from typing import TYPE_CHECKING

from structlog import get_logger

from passbook.lib.utils.template import render_to_string
from passbook.providers.saml.xml_signing import (get_signature_xml,
                                                 sign_with_signxml)

if TYPE_CHECKING:
    from passbook.providers.saml.models import SAMLProvider

LOGGER = get_logger()


def _get_attribute_statement(params):
    """Inserts AttributeStatement, if we have any attributes.
    Modifies the params dict.
    PRE-REQ: params['SUBJECT'] has already been created (usually by a call to
    _get_subject()."""
    attributes = params.get('ATTRIBUTES', [])
    if not attributes:
        params['ATTRIBUTE_STATEMENT'] = ''
        return
    # Build complete AttributeStatement.
    params['ATTRIBUTE_STATEMENT'] = render_to_string('saml/xml/attributes.xml', {
        'attributes': attributes})


def _get_in_response_to(params):
    """Insert InResponseTo if we have a RequestID.
    Modifies the params dict."""
    # NOTE: I don't like this. We're mixing templating logic here, but the
    # current design requires this; maybe refactor using better templates, or
    # just bite the bullet and use elementtree to produce the XML; see comments
    # in xml_templates about Canonical XML.
    request_id = params.get('REQUEST_ID', None)
    if request_id:
        params['IN_RESPONSE_TO'] = 'InResponseTo="%s" ' % request_id
    else:
        params['IN_RESPONSE_TO'] = ''


def _get_subject(params):
    """Insert Subject. Modifies the params dict."""
    params['SUBJECT_STATEMENT'] = render_to_string('saml/xml/subject.xml', params)


def get_assertion_xml(template, parameters, signed=False):
    """Get XML for Assertion"""
    # Reset signature.
    params = {}
    params.update(parameters)
    params['ASSERTION_SIGNATURE'] = ''

    _get_in_response_to(params)
    _get_subject(params)  # must come before _get_attribute_statement()
    _get_attribute_statement(params)

    unsigned = render_to_string(template, params)
    # LOGGER.debug('Unsigned: %s', unsigned)
    if not signed:
        return unsigned

    # Sign it.
    signature_xml = get_signature_xml()
    params['ASSERTION_SIGNATURE'] = signature_xml
    return render_to_string(template, params)


def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=''):
    """Returns XML for response, with signatures, if signed is True."""
    # Reset signatures.
    params = {}
    params.update(parameters)
    params['RESPONSE_SIGNATURE'] = ''
    _get_in_response_to(params)

    raw_response = render_to_string('saml/xml/response.xml', params)

    # LOGGER.debug('Unsigned: %s', unsigned)
    if not saml_provider.signing:
        return raw_response

    signature_xml = get_signature_xml()
    params['RESPONSE_SIGNATURE'] = signature_xml
    # LOGGER.debug("Raw response: %s", raw_response)

    signed = sign_with_signxml(
        saml_provider.signing_key, raw_response, saml_provider.signing_cert,
        reference_uri=assertion_id)
    return signed