# server.py

import argparse
import logging
import os
import signal
import sys
from datetime import datetime
from pathlib import Path

from axidrawinternal.plot_utils_import import from_dependency_import
from flask import Flask, g, request

import axi_control as axc

SRV_OPTS = 'config/srv_options.json'
PID_FILE = Path('/tmp/axi_server.pid')

LOCKED_ENDPOINTS = {
    'nudge_x',
    'nudge_y',
    'home',
    'move_xy',
    'move_to',
    'set_home',
    'toggle_pen',
    'cycle_pen',
    'disable_motors',
    'enable_motors',
    'prime',
    'motor_state',
}


# ---------- setup paths & logging ----------
log_dir = Path(os.getcwd()) / 'logs'
log_dir.mkdir(parents=True, exist_ok=True)

timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
log_filename = log_dir / 'axiserver.log'
plot_status_log_filename = log_dir / 'plot_status.log'

logging.basicConfig(
    filename=log_filename,
    filemode='w',
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s',
)

# logging.getLogger('werkzeug').addFilter(axc.utils.RequestFilter())
SKIP_LOG_PATHS = {}  # {'/log', '/axidraw_status', '/motor_state'}
# ˆˆˆ these tend to be very frequent and clutter the access log. replaces above
logging.getLogger('werkzeug').setLevel(logging.WARNING)
ACCESS_LOGGER = logging.getLogger('axi_access')


# ---------- app & controller ----------
def create_app(artwork_root: str | None):
    file_root = axc.utils.select_file_root(artwork_root)
    app = Flask(__name__)
    ad = axc.AxiController()
    ebb_serial = from_dependency_import('plotink.ebb_serial')
    ctx = axc.ServerContext(
        ad=ad,
        ebb_serial=ebb_serial,
        file_root=file_root,
        srv_opts_path=SRV_OPTS,
        log_filename=log_filename,
        plot_status_log_filename=plot_status_log_filename,
    )

    @app.before_request
    def _lock_ad_access():
        if request.endpoint in LOCKED_ENDPOINTS:
            ctx.ad_lock.acquire()
            g.ad_lock_acquired = True

    @app.teardown_request
    def _unlock_ad_access(_exc):
        if getattr(g, 'ad_lock_acquired', False):
            ctx.ad_lock.release()

    @app.after_request
    def _log_access(response):
        if request.path in SKIP_LOG_PATHS:
            return response
        remote_addr = request.remote_addr or '-'
        path = request.full_path.rstrip('?')
        size = response.calculate_content_length()
        size_text = size if size is not None else '-'
        ACCESS_LOGGER.info(
            '%s "%s %s" %s %s',
            remote_addr,
            request.method,
            path,
            response.status_code,
            size_text,
        )
        return response

    axc.control.register(app, ctx)
    axc.status.register(app, ctx)
    axc.plotting.register(app, ctx)
    return app, ctx


def cleanup(signum=None, frame=None):
    logging.info('Cleaning up, signal=%s', signum)
    PID_FILE.unlink(missing_ok=True)
    sys.exit(0)


# ---------- entry point ----------
def _parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description='AxiControl Flask server')
    parser.add_argument(
        '--dev',
        action='store_true',
        help='Skip hardware connection checks for local development',
    )
    parser.add_argument(
        '--artwork-root',
        default=None,
        help='Root directory for artwork SVGs (overrides ARTWORK_ROOT).',
    )
    return parser.parse_args()


if __name__ == '__main__':
    args = _parse_args()
    app, ctx = create_app(args.artwork_root)
    logging.info('Artwork root set to %s', ctx.file_root)

    if not args.dev:
        if not ctx.ad.connect():
            logging.error('AxiController connection failed')
            raise RuntimeError('AxiController connection failed')
    # write PID only for the real server process
    PID_FILE.write_text(str(os.getpid()))

    # clean shutdown
    signal.signal(signal.SIGTERM, cleanup)
    signal.signal(signal.SIGINT, cleanup)

    app.run(host='0.0.0.0', port=5050, threaded=True)
