"""
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016-2024 Jason Antman <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
"""

import os
import time
from werkzeug.serving import run_simple, WSGIRequestHandler
from flask.cli import pass_script_info

import click


class CustomLoggingWSGIRequestHandler(WSGIRequestHandler):
    """Extend werkzeug request handler to include processing time in logs"""

    def handle(self):
        self._time_started = time.time()
        rv = super(CustomLoggingWSGIRequestHandler, self).handle()
        return rv

    def send_response(self, *args, **kw):
        self._time_finished = time.time()
        super(CustomLoggingWSGIRequestHandler, self).send_response(*args, **kw)

    def log_request(self, code='-', size='-'):
        duration = int((self._time_finished - self._time_started) * 1000)
        self.log(
            'info', '"%s" %s %s [%sms]',
            self.requestline, code, size, duration
        )


def template_paths():
    """
    Return a list of all Flask app template paths, to auto-reload on change.

    from http://stackoverflow.com/a/41666467/211734

    :return: list of all template paths
    :rtype: list
    """
    extra_dirs = [
        os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'templates'
        ),
    ]

    extra_files = extra_dirs[:]
    for extra_dir in extra_dirs:
        for dirname, dirs, files in os.walk(extra_dir):
            for filename in files:
                filename = os.path.join(dirname, filename)
                if os.path.isfile(filename):
                    extra_files.append(filename)
    return extra_files


@click.command('rundev', short_help='Runs a development server with reloading')
@click.option('--host', '-h', default='127.0.0.1',
              help='The interface to bind to.')
@click.option('--port', '-p', default=5000,
              help='The port to bind to.')
@click.option('--with-threads/--without-threads', default=False,
              help='Enable or disable multithreading.')
@pass_script_info
def rundev_command(info, host, port, with_threads):
    """
    Modified from the upstream ``flask.cli.run_command`` to add some debugging
    and reloading features
    """
    # Set a global flag that indicates that we were invoked from the
    # command line interface provided server command.  This is detected
    # by Flask.run to make the call into a no-op.  This is necessary to
    # avoid ugly errors when the script that is loaded here also attempts
    # to start a server.
    os.environ['FLASK_RUN_FROM_CLI_SERVER'] = '1'

    os.environ['FLASK_DEBUG'] = '1'

    debug = True
    reload = True
    debugger = True

    # Load the Flask app directly (DispatchingApp was removed in Flask 2.3)
    app = info.load_app()

    # Extra startup messages.  This depends a bit on Werkzeug internals to
    # not double execute when the reloader kicks in.
    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
        # If we have an import path we can print it out now which can help
        # people understand what's being served.  If we do not have an
        # import path because the app was loaded through a callback then
        # we won't print anything.
        if info.app_import_path is not None:
            print(' * Serving Flask app "%s"' % info.app_import_path)
        if debug is not None:
            print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))

    run_simple(
        host, port, app, use_reloader=reload, use_debugger=debugger,
        threaded=with_threads, extra_files=template_paths(),
        request_handler=CustomLoggingWSGIRequestHandler
    )
