text: # This file is part of datacube-ows, part of the Open Data Cube project.
  # See https://opendatacube.org for more information.
  #
  # Copyright (c) 2017-2021 OWS Contributors
  # SPDX-License-Identifier: Apache-2.0
  import sys
  import traceback
  from time import monotonic
  
  from flask import g, render_template, request
  from flask_log_request_id import current_request_id
  
  from datacube_ows import __version__
  from datacube_ows.cube_pool import cube
  from datacube_ows.legend_generator import create_legend_for_style
  from datacube_ows.ogc_exceptions import OGCException, WMSException
  from datacube_ows.ogc_utils import (capture_headers, get_service_base_url,
                                      lower_get_args, resp_headers)
  from datacube_ows.ows_configuration import get_config
  from datacube_ows.protocol_versions import supported_versions
  # See startup_utils.py for initialisation methods called at startup.
  # pylint: disable=wildcard-import,unused-wildcard-import
  from datacube_ows.startup_utils import *
  from datacube_ows.wcs1 import WCS_REQUESTS
  from datacube_ows.wms import WMS_REQUESTS
  
  # Logging intialisation
  _LOG = initialise_logger()
  initialise_ignorable_warnings()
  
  # Initialisation of external libraries - controlled by environment variables.
  initialise_debugging(_LOG)
  initialise_sentry(_LOG)
  initialise_aws_credentials(_LOG)
  
  # Prepare parsed configuration object
  parse_config_file()
  
  # Initialise Flask
  app = initialise_flask(__name__)
  
  # Initialisation of external libraries that depend on Flask
  # (controlled by environment variables)
  metrics = initialise_prometheus(app, _LOG)
  
  # Protocol/Version lookup table
  OWS_SUPPORTED = supported_versions()
  
  # Flask Routes
  
  
  @app.route('/')
  def ogc_impl():
      #pylint: disable=too-many-branches
      nocase_args = lower_get_args()
      nocase_args = capture_headers(request, nocase_args)
      service = nocase_args.get("service", "").upper()
      if service:
          return ogc_svc_impl(service.lower())
  
      # create dummy env if not exists
      try:
          # service argument is only required (in fact only defined) by OGC for
          # GetCapabilities requests.  As long as we are persisting with a single
          # routing end point for all services, we must derive the service from the request
          # parameter.
          # This is a quick hack to fix #64.  Service and operation routing could be
          # handled more elegantly.
          op = nocase_args.get("request", "").upper()
          if op in WMS_REQUESTS:
              return ogc_svc_impl("wms")
          elif op in WCS_REQUESTS:
              return ogc_svc_impl("wcs")
          elif op:
              # Should we return a WMS or WCS exception if there is no service specified?
              # Defaulting to WMS because that's what we already have.
              raise WMSException("Invalid service and/or request", locator="Service and request parameters")
          else:
              cfg = get_config()
              url = nocase_args.get('Host', nocase_args['url_root'])
              base_url = get_service_base_url(cfg.allowed_urls, url)
              return (render_template(
                              "index.html",
                              cfg=cfg,
                              supported=OWS_SUPPORTED,
                              base_url=base_url,
                              version=__version__,
                      ),
                      200,
                      resp_headers({"Content-Type": "text/html"}))
      except OGCException as e:
          _LOG.error("Handled Error: %s", repr(e.errors))
          return e.exception_response()
      except Exception as e: # pylint: disable=broad-except
          tb = sys.exc_info()[2]
          ogc_e = WMSException("Unexpected server error: %s" % str(e), http_response=500)
          return ogc_e.exception_response(traceback=traceback.extract_tb(tb))
  
  
  def ogc_svc_impl(svc):
      svc_support = OWS_SUPPORTED.get(svc)
      nocase_args = lower_get_args()
      nocase_args = capture_headers(request, nocase_args)
      service = nocase_args.get("service", svc).upper()
  
      # Is service activated in config?
      try:
          if not svc_support:
              raise WMSException(f"Invalid service: {svc}",
                                 valid_keys=[
                                         service.service
                                         for service in OWS_SUPPORTED.values()
                                         if service.activated()
                                 ],
                                 code=WMSException.OPERATION_NOT_SUPPORTED,
                                 locator="service parameter")
          if not svc_support.activated():
              raise svc_support.default_exception_class("Invalid service and/or request", locator="Service and request parameters")
  
          # Does service match path (if supplied)
          if service != svc_support.service_upper:
              raise svc_support.default_exception_class("Invalid service", locator="Service parameter")
  
          version = nocase_args.get("version")
          version_support = svc_support.negotiated_version(version)
      except OGCException as e:
          return e.exception_response()
  
      try:
          return version_support.router(nocase_args)
      except OGCException as e:
          return e.exception_response()
      except Exception as e: #pylint: disable=broad-except
          tb = sys.exc_info()[2]
          ogc_e = version_support.exception_class("Unexpected server error: %s" % str(e), http_response=500)
          return ogc_e.exception_response(traceback=traceback.extract_tb(tb))
  
  
  @app.route('/wms')
  def ogc_wms_impl():
      return ogc_svc_impl("wms")
  
  
  @app.route('/wmts')
  def ogc_wmts_impl():
      return ogc_svc_impl("wmts")
  
  
  @app.route('/wcs')
  def ogc_wcs_impl():
      return ogc_svc_impl("wcs")
  
  
  @app.route('/ping')
  def ping():
      db_ok = False
      with cube() as dc:
          if dc:
              # pylint: disable=protected-access
              with dc.index._db.give_me_a_connection() as conn:
                  results = conn.execute("""
                          SELECT *
                          FROM wms.product_ranges
                          LIMIT 1"""
                  )
                  for r in results:
                      db_ok = True
      if db_ok:
          return (render_template("ping.html", status="Up"), 200, resp_headers({"Content-Type": "text/html"}))
      else:
          return (render_template("ping.html", status="Down"), 500, resp_headers({"Content-Type": "text/html"}))
  
  
  @app.route("/legend/<string:layer>/<string:style>/legend.png")
  def legend(layer, style, dates=None):
      cfg = get_config()
      product = cfg.product_index.get(layer)
      if not product:
          return ("Unknown Layer", 404, resp_headers({"Content-Type": "text/plain"}))
      if dates is None:
          args = lower_get_args()
          ndates = int(args.get("ndates", 0))
      else:
          ndates = len(dates)
      try:
          img = create_legend_for_style(product, style, ndates)
      except WMSException as e:
          return (str(e), e.http_response, resp_headers({"Content-Type": "text/plain"}))
  
      if not img:
          return ("Unknown Style", 404, resp_headers({"Content-Type": "text/plain"}))
      return img
  
  # Flask middleware
  
  
  @app.after_request
  def append_request_id(response):
      response.headers.add("X-REQUEST-ID", current_request_id())
      return response
  
  
  @app.before_request
  def start_timer():
      # pylint: disable=assigning-non-slot
      g.ogc_start_time = monotonic()
  
  
  @app.after_request
  def log_time_and_request_response(response):
      time_taken = int((monotonic() - g.ogc_start_time) * 1000)
      _LOG.info("request: %s returned status: %d and took: %d ms", request.url, response.status_code, time_taken)
      return response
  
  
  # Note: register your default metrics after all routes have been set up.
  # Also note, that Gauge metrics registered as default will track the /metrics endpoint, and this can't be disabled at the moment.
  initialise_prometheus_register(metrics)
  