"""Churchill module - Winston application launcher with restart management.

This module serves as the entry point for the Winston application, providing
crash recovery and automatic restart capabilities. Named after Winston Churchill,
it manages the lifecycle of the main Winston application (clem.py) and handles:
- Automatic restart on crash (up to configurable threshold)
- Module cache clearing between restarts
- Output stream management and cleanup
- User-requested vs exception-triggered restart tracking

The ChurchillManager monitors exceptions and automatically restarts Winston
up to a configured number of times before giving up. This provides resilience
against transient errors while preventing infinite restart loops.

Key Components:
    ChurchillManager: Main restart manager class
    clear_environment: Clears loaded modules for clean restart
    main: Package entry point function

Example:
    Run Winston with restart management::

        python -m winston.churchill
        # or
        from winston.churchill import main
        main()

Typical usage:
    This module is the recommended entry point for running Winston.
    It provides better error recovery than running clem.py directly.
"""

from __future__ import annotations
from typing import Any
import typeguard  # type: ignore[import]
# hook=typeguard.install_import_hook()
import winsound
winsound.Beep(350, 200)
import os
import sys
import traceback as tb
import importlib


def clear_environment(main_dir: str) -> None:
    """Remove modules loaded from main directory from sys.modules.

    Clears Winston modules from the import cache to enable clean reload
    after crash or restart. This prevents stale module state from persisting
    across restart cycles.

    Args:
        main_dir: Path to the main directory where primary modules are located.

    Note:
        Preserves the 'clem' module as it's the main entry point.
        Skips built-in modules and modules without __file__ attribute.
        Failures on individual modules are silently ignored.

    Example:
        >>> clear_environment(os.path.dirname(__file__))
        # Clears all Winston modules except clem from sys.modules
    """
    main_dir_abs = os.path.abspath(main_dir)
    modules_to_remove: list[str] = []
    for module_name, module in sys.modules.items():
        try:
            # Check if the module has a __file__ attribute and if it comes from the main directory
            if hasattr(module, '__file__') and module.__file__ is not None:
                if module.__file__.startswith(main_dir_abs):
                    if module_name != 'clem':
                        modules_to_remove.append(module_name)
        except Exception:
            # Skip modules that cause issues (e.g., built-in modules without __file__)
            continue
    # Remove identified modules
    for module_name in modules_to_remove:
        del sys.modules[module_name]


class ChurchillManager:
    """Manager for Winston application restart and crash recovery.

    Monitors Winston application lifecycle, automatically restarting on
    crashes up to a configurable threshold. Tracks both user-initiated
    restarts (clean) and exception-triggered restarts (crashes).

    The manager implements a restart loop with exception counting to prevent
    infinite restart cycles while still providing resilience against transient
    errors.

    Attributes:
        user_restart: True if user requested restart (clean exit).
        exception_threshold: Maximum number of crash restarts before giving up.
        exception_count: Current count of consecutive crash restarts.
        exception_restart: True if last restart was due to exception.

    Example:
        >>> manager = ChurchillManager()
        >>> manager.run()
        # Winston runs with automatic restart on crash (up to 3 times)
    """

    def __init__(self) -> None:
        """Initialize ChurchillManager with default restart settings.

        Sets up restart tracking with:
        - User restart enabled (allows clean restarts)
        - Exception threshold of 3 (max crash restarts)
        - Exception counters at 0
        """
        self.user_restart: bool = True
        self.exception_threshold: int = 3
        self.exception_count: int = 0
        self.exception_restart: bool = False

    def run(self) -> None:
        """Run Winston in a restart loop with crash recovery.

        Continuously runs Winston (clem.main) while restart conditions are met.
        On each iteration:
        1. Clears module cache for clean reload
        2. Reloads and runs clem.main()
        3. Handles exceptions with automatic restart (up to threshold)
        4. Restores stdout/stderr on exit or restart

        The loop continues while:
        - User restart is enabled (user requested restart), OR
        - Exception restart is enabled AND exception count <= threshold

        Note:
            Flushes stdout/stderr before restart to ensure output is written.
            Always restores original stdout/stderr in finally block.
        """
        while self.restart():
            try:
                clear_environment(os.path.dirname(__file__))
                from . import clem
                importlib.reload(clem)
                user_restart = clem.main(restartable=True)
                self.set_user_restart(user_restart)
            except Exception:
                tb.print_exc()
                # provide the below string to clem.main and display it on reload
                exception_str = 'Before restart clem exception: ', tb.format_exc()
                self.set_exception_restart(True)
            finally:
                # flushing sys.stdout should ensure all output gets written to output.txt
                # before winston departs or restarts
                sys.stdout.flush()
                sys.stderr.flush()
                # reset stdout and stderr back to original values before restart
                sys.stdout = sys.__stdout__
                sys.stderr = sys.__stderr__

    def set_user_restart(self, restart: bool = True) -> None:
        """Set user restart flag and optionally reset exception counter.

        Args:
            restart: If True, enable user restart and reset exception count
                to 0 (indicating clean exit). If False, disable user restart.
                Defaults to True.

        Note:
            Resetting exception count on clean restart allows full threshold
            of crash restarts after each successful run.
        """
        if restart:
            self.exception_count = 0
        self.user_restart = restart

    def set_exception_restart(self, restart: bool = True) -> None:
        """Set exception restart flag and increment exception counter.

        Args:
            restart: If True, enable exception restart and increment count.
                If False, disable exception restart. Defaults to True.

        Note:
            Exception count is only incremented when restart=True, tracking
            consecutive crashes for threshold checking.
        """
        if restart:
            self.exception_count += 1
        self.exception_restart = restart

    def restart(self) -> bool:
        """Determine if Winston should restart.

        Returns:
            True if restart should occur, False otherwise.

        Restart occurs if:
        - user_restart is True (user requested clean restart), OR
        - exception_restart is True AND exception_count <= threshold

        Note:
            This implements the core restart logic, allowing either clean
            user restarts (unlimited) or crash restarts (up to threshold).
        """
        return self.user_restart or (
            self.exception_restart and (self.exception_count <= self.exception_threshold)
        )


if __name__ == '__main__':
    runner = ChurchillManager()
    runner.run()
    # outputs should have been reset in finally clause of run() so below probably redundant
    sys.stdout = sys.__stdout__
    sys.stderr = sys.__stderr__


def main() -> None:
    """Main entry point for winston package.

    Creates ChurchillManager and runs Winston with automatic restart
    and crash recovery. This is the recommended entry point for the
    Winston application.

    Can be called from:
    - Command line: python -m winston.churchill
    - Package import: from winston.churchill import main; main()
    - setup.py console_scripts entry point

    Note:
        Restores stdout/stderr after run completes (redundant with
        finally block in ChurchillManager.run()).
    """
    runner = ChurchillManager()
    runner.run()
    # Outputs should have been reset in finally clause of run() so below probably redundant
    sys.stdout = sys.__stdout__
    sys.stderr = sys.__stderr__
