# encoding: utf-8

import falcon
import ipaddress
import os
from certidude import config
from user_agents import parse


class NormalizeMiddleware(object):
    def process_request(self, req, resp, *args):
        req.context["remote_addr"] = ipaddress.ip_address(req.access_route[0])
        if req.user_agent:
            req.context["user_agent"] = parse(req.user_agent)
        else:
            req.context["user_agent"] = "Unknown user agent"

def certidude_app(log_handlers=[]):
    from certidude import authority, config
    from certidude.tokens import TokenManager
    from .signed import SignedCertificateDetailResource
    from .request import RequestListResource, RequestDetailResource
    from .lease import LeaseResource, LeaseDetailResource
    from .script import ScriptResource
    from .tag import TagResource, TagDetailResource
    from .attrib import AttributeResource
    from .bootstrap import BootstrapResource
    from .token import TokenResource
    from .builder import ImageBuilderResource
    from .session import SessionResource, CertificateAuthorityResource

    app = falcon.API(middleware=NormalizeMiddleware())
    app.req_options.auto_parse_form_urlencoded = True

    # Certificate authority API calls
    app.add_route("/api/certificate/", CertificateAuthorityResource())
    app.add_route("/api/signed/{cn}/", SignedCertificateDetailResource(authority))
    app.add_route("/api/request/{cn}/", RequestDetailResource(authority))
    app.add_route("/api/request/", RequestListResource(authority))

    token_resource = None
    token_manager = None
    if config.USER_ENROLLMENT_ALLOWED: # TODO: add token enable/disable flag for config
        if config.TOKEN_BACKEND == "sql":
            token_manager = TokenManager(config.TOKEN_DATABASE)
            token_resource = TokenResource(authority, token_manager)
            app.add_route("/api/token/", token_resource)
        elif not config.TOKEN_BACKEND:
            pass
        else:
            raise NotImplementedError("Token backend '%s' not supported" % config.TOKEN_BACKEND)

    app.add_route("/api/", SessionResource(authority, token_manager))

    # Extended attributes for scripting etc.
    app.add_route("/api/signed/{cn}/attr/", AttributeResource(authority, namespace="machine"))
    app.add_route("/api/signed/{cn}/script/", ScriptResource(authority))

    # API calls used by pushed events on the JS end
    app.add_route("/api/signed/{cn}/tag/", TagResource(authority))
    app.add_route("/api/signed/{cn}/lease/", LeaseDetailResource(authority))

    # API call used to delete existing tags
    app.add_route("/api/signed/{cn}/tag/{tag}/", TagDetailResource(authority))

    # Gateways can submit leases via this API call
    app.add_route("/api/lease/", LeaseResource(authority))

    # Bootstrap resource
    app.add_route("/api/bootstrap/", BootstrapResource(authority))

    # LEDE image builder resource
    app.add_route("/api/build/{profile}/{suggested_filename}", ImageBuilderResource())

    # Add CRL handler if we have any whitelisted subnets
    if config.CRL_SUBNETS:
        from .revoked import RevocationListResource
        app.add_route("/api/revoked/", RevocationListResource(authority))

    # Add SCEP handler if we have any whitelisted subnets
    if config.SCEP_SUBNETS:
        from .scep import SCEPResource
        app.add_route("/api/scep/", SCEPResource(authority))

    if config.OCSP_SUBNETS:
        from .ocsp import OCSPResource
        app.add_sink(OCSPResource(authority), prefix="/api/ocsp")

    # Set up log handlers
    if config.LOGGING_BACKEND == "sql":
        from certidude.mysqllog import LogHandler
        from certidude.api.log import LogResource
        uri = config.cp.get("logging", "database")
        log_handlers.append(LogHandler(uri))
        app.add_route("/api/log/", LogResource(uri))
    elif config.LOGGING_BACKEND == "syslog":
        from logging.handlers import SysLogHandler
        log_handlers.append(SysLogHandler())
        # Browsing syslog via HTTP is obviously not possible out of the box
    elif config.LOGGING_BACKEND:
        raise ValueError("Invalid logging.backend = %s" % config.LOGGING_BACKEND)

    return app
