# Source code for scitex_dev.dashboard.app

    #!/usr/bin/env python3
    # Timestamp: 2026-02-02
    # File: scitex_dev/dashboard/app.py

    """Flask application factory for the dashboard."""

    from __future__ import annotations

    from typing import TYPE_CHECKING

    if TYPE_CHECKING:
        from flask import Flask

    def create_app() -> Flask:
        """Create and configure the Flask application.

        Returns
        -------
        Flask
            Configured Flask application.
        """
        try:
            from flask import Flask
        except ImportError as e:
            raise ImportError(
                "Flask is required for the dashboard. Install with: pip install flask"
            ) from e

        from pathlib import Path

        static_folder = Path(__file__).parent / "static"
        app = Flask(__name__, static_folder=str(static_folder), static_url_path="/static")

        # Disable JSON key sorting to preserve insertion order (Flask 2.2+)
        app.json.sort_keys = False

        # Register routes
        from .routes import register_routes

        register_routes(app)

        return app

    def _kill_process_on_port(port: int) -> None:
        """Kill any process using the specified port.

        Parameters
        ----------
        port : int
            Port number to free up.
        """
        import subprocess
        import sys

        try:
            if sys.platform == "win32":
                # Windows: use netstat and taskkill
                result = subprocess.run(
                    ["netstat", "-ano"],
                    capture_output=True,
                    text=True,
                    check=False,
                )
                for line in result.stdout.splitlines():
                    if f":{port}" in line and "LISTENING" in line:
                        pid = line.strip().split()[-1]
                        subprocess.run(
                            ["taskkill", "/F", "/PID", pid],
                            capture_output=True,
                            check=False,
                        )
                        print(f"Killed process {pid} on port {port}")
            else:
                # Unix: use lsof
                result = subprocess.run(
                    ["lsof", "-ti", f":{port}"],
                    capture_output=True,
                    text=True,
                    check=False,
                )
                if result.stdout.strip():
                    pids = result.stdout.strip().split("\n")
                    for pid in pids:
                        subprocess.run(
                            ["kill", "-9", pid], capture_output=True, check=False
                        )
                        print(f"Killed process {pid} on port {port}")
        except Exception as e:
            print(f"Warning: Could not kill process on port {port}: {e}")

    def run_dashboard(
        host: str = "0.0.0.0",
        port: int = 5000,
        debug: bool = False,
        open_browser: bool = True,
        force: bool = False,
    ) -> None:
        """Run the Flask dashboard server.

        Parameters
        ----------
        host : str
            Host to bind to. Default "127.0.0.1".
        port : int
            Port to listen on. Default 5000.
        debug : bool
            Enable Flask debug mode.
        open_browser : bool
            Open browser automatically.
        force : bool
            Kill existing process using the port if any.
        """
        if force:
            _kill_process_on_port(port)

        app = create_app()

        url = f"http://{host}:{port}"
        print(f"Starting SciTeX Version Dashboard at {url}")
        print("Press Ctrl+C to stop.")

        if open_browser:
            import threading
            import webbrowser

            def open_url():
                import time

                time.sleep(1)  # Wait for server to start
                webbrowser.open(url)

            threading.Thread(target=open_url, daemon=True).start()

        try:
            app.run(host=host, port=port, debug=debug, threaded=True)
        except KeyboardInterrupt:
            print("\nDashboard stopped.")

    def run_background(
        host: str = "0.0.0.0",
        port: int = 5000,
        force: bool = False,
    ) -> None:
        """Launch the dashboard as a detached background subprocess.

        Parameters
        ----------
        host : str
            Host to bind to. Default "127.0.0.1".
        port : int
            Port to listen on. Default 5000.
        force : bool
            Kill existing process using the port if any.
        """
        import subprocess
        import sys

        from scitex_config._ecosystem import local_state

        # Runtime-only (per 01_arch_06_local-state-directories.md §1):
        # logs + pid files go under `runtime/`, never committed to git.
        # local_state.runtime_path() auto-creates runtime/ + .gitkeep + README.md.
        runtime_dir = local_state.runtime_path("dev")

        log_path = runtime_dir / "dashboard.log"
        pid_path = runtime_dir / "dashboard.pid"

        inline_script = (
            f"from scitex_dev.dashboard.app import run_dashboard; "
            f"run_dashboard(host={host!r}, port={port!r}, debug=False, open_browser=False, force={force!r})"
        )

        log_file = open(log_path, "a")
        proc = subprocess.Popen(
            [sys.executable, "-c", inline_script],
            stdout=log_file,
            stderr=log_file,
            start_new_session=True,
        )

        pid_path.write_text(str(proc.pid))

    def stop_dashboard() -> bool:
        """Stop a running background dashboard process.

        Returns
        -------
        bool
            True if the process was successfully stopped, False otherwise.
        """
        import os
        import signal
        from pathlib import Path

        pid_path = Path.home() / ".scitex" / "dev" / "runtime" / "dashboard.pid"

        if not pid_path.exists():
            print("No dashboard PID file found. Is the dashboard running in background?")
            return False

        try:
            pid = int(pid_path.read_text().strip())
            os.kill(pid, signal.SIGTERM)
            pid_path.unlink()
            print(f"Dashboard (PID {pid}) stopped.")
            return True
        except ProcessLookupError:
            print("Process not found. Removing stale PID file.")
            pid_path.unlink(missing_ok=True)
            return False
        except Exception as e:
            print(f"Error stopping dashboard: {e}")
            return False

    # EOF