# status.py

import logging

from flask import jsonify, request, send_file

from .. import ut


def register(app, ctx) -> None:
    @app.route('/motor_state')
    def motor_state():
        if ctx.plot_in_progress:
            return jsonify({'motors_on': None, 'unknown': True, 'plotting': True})
        state = ctx.ad.motor_state()
        return jsonify({'motors_on': state, 'unknown': state is None, 'plotting': False})

    @app.route('/plot_status')
    def plot_status():
        with ctx.plot_state_lock:
            state = dict(ctx.plot_state)

        total_layers = max(int(state.get('total_layers') or 0), 0)
        current_layer = max(int(state.get('current_layer') or 0), 0)
        progress_mm = float(state.get('progress_mm') or 0.0)
        total_mm = float(state.get('total_mm') or 0.0)
        layer_percent = 0.0 if total_mm <= 0 else min(progress_mm / total_mm, 1.0)
        if total_layers > 0:
            overall_percent = min(((current_layer - 1) + layer_percent) / total_layers, 1.0)
        else:
            overall_percent = layer_percent

        state.update(
            {
                'progress_mm': progress_mm,
                'total_mm': total_mm,
                'layer_percent': layer_percent,
                'overall_percent': overall_percent,
            }
        )
        return jsonify(state)

    @app.route('/log')
    def get_log() -> str:
        return ctx.log_filename.read_text()

    @app.route('/plot_status_log', methods=['POST'])
    def plot_status_log() -> tuple[str, int]:
        payload = request.get_json(silent=True) or {}
        line = payload.get('line', '')
        if not isinstance(line, str) or not line.strip():
            return ('missing line', 400)
        with ctx.plot_status_log_filename.open('a', encoding='utf-8') as handle:
            handle.write(line.rstrip('\n') + '\n')
        return ('ok', 200)

    @app.route('/axidraw_status')
    def axidraw_status():
        if ctx.ad.connected:
            return jsonify({'connected': True})
        port = None
        connected = False
        try:
            port = ctx.ebb_serial.openPort()
            connected = port is not None
        except Exception:
            logging.exception('AxiDraw status check failed')
            connected = False
        finally:
            if port is not None:
                try:
                    ctx.ebb_serial.closePort(port)
                except Exception:
                    logging.exception('AxiDraw status close failed')
        return jsonify({'connected': connected})

    @app.route('/srv_options', methods=['GET'])
    def get_srv_options():
        ctx.srv_options = ut.load_srv_options(ctx.srv_opts_path)
        ctx.ad.apply_srv_options(ctx.srv_options)
        return jsonify(ctx.srv_options)

    @app.route('/srv_options', methods=['POST'])
    def update_srv_options():
        payload = request.get_json(silent=True) or {}
        if not payload:
            return 'No JSON payload provided', 400

        next_options = ut.load_srv_options(ctx.srv_opts_path)
        limits = {
            'pen_pos_up': (0, 100),
            'pen_pos_down': (0, 100),
            'speed_penup': (1, 100),
            'speed_pendown': (1, 100),
            'accel': (1, 100),
            'pen_rate_raise': (1, 100),
            'pen_rate_lower': (1, 100),
            'pen_delay_up': (0, 10000),
            'pen_delay_down': (0, 10000),
            'model': (1, 7),
            'reordering': (0, 4),
        }
        for key, (min_value, max_value) in limits.items():
            if key in payload:
                try:
                    value = int(payload[key])
                except (TypeError, ValueError):
                    return f'Invalid value for {key}', 400
                next_options[key] = ut.clamp_int(value, min_value, max_value)

        bool_keys = ['const_speed', 'auto_rotate', 'random_start']
        for key in bool_keys:
            if key not in payload:
                continue
            value = payload[key]
            if isinstance(value, bool):
                next_options[key] = value
            elif isinstance(value, (int, float)) and value in (0, 1):
                next_options[key] = bool(value)
            elif isinstance(value, str):
                normalized = value.strip().lower()
                if normalized in ('true', '1'):
                    next_options[key] = True
                elif normalized in ('false', '0'):
                    next_options[key] = False
                else:
                    return f'Invalid value for {key}', 400
            else:
                return f'Invalid value for {key}', 400

        if (
            'pen_pos_up' in next_options
            and 'pen_pos_down' in next_options
            and next_options['pen_pos_up'] < next_options['pen_pos_down']
        ):
            next_options['pen_pos_up'] = next_options['pen_pos_down']

        ut.write_srv_options(next_options, ctx.srv_opts_path)
        ctx.srv_options = next_options
        ctx.ad.apply_srv_options(ctx.srv_options)
        return jsonify(ctx.srv_options)

    @app.route('/list_dir', methods=['GET'])
    def list_dir():
        rel_path = request.args.get('path', '')
        target = ut.resolve_dir_path(ctx.file_root, rel_path)
        if target is None:
            return jsonify({'error': 'Invalid path'}), 400
        if not target.exists():
            return jsonify({'error': 'Path not found'}), 404
        if not target.is_dir():
            return jsonify({'error': 'Path is not a directory'}), 400

        folders = []
        files = []
        for entry in target.iterdir():
            if entry.is_dir():
                folders.append(entry.name)
            elif entry.is_file():
                files.append(entry.name)

        return jsonify(
            {
                'path': str(target.relative_to(ctx.file_root)),
                'folders': sorted(folders),
                'files': sorted(files),
            }
        )

    @app.route('/file', methods=['GET'])
    def get_file():
        rel_path = request.args.get('path', '')
        target = ut.resolve_file_path(ctx.file_root, rel_path)
        if target is None:
            return jsonify({'error': 'Invalid path'}), 400
        if not target.exists():
            return jsonify({'error': 'Path not found'}), 404
        if not target.is_file():
            return jsonify({'error': 'Path is not a file'}), 400

        mimetype = 'image/svg+xml' if target.suffix.lower() == '.svg' else None
        return send_file(target, mimetype=mimetype)
