# [DEF:backend.tests.scripts.test_clean_release_tui:Module]
# @TIER: STANDARD
# @SEMANTICS: tests, tui, clean-release, curses
# @PURPOSE: Unit tests for the interactive curses TUI of the clean release process.
# @LAYER: Scripts
# @RELATION: TESTS -> backend.src.scripts.clean_release_tui
# @INVARIANT: TUI initializes, handles hotkeys (F5, F10) and safely falls back without TTY.

import os
import sys
import curses
from unittest import mock
from unittest.mock import MagicMock, patch

import pytest

from src.scripts.clean_release_tui import CleanReleaseTUI, main, tui_main
from src.models.clean_release import CheckFinalStatus


@pytest.fixture
def mock_stdscr() -> MagicMock:
    stdscr = MagicMock()
    stdscr.getmaxyx.return_value = (40, 100)
    stdscr.getch.return_value = -1
    return stdscr


def test_headless_fallback(capsys):
    """
    @TEST_EDGE: stdout_unavailable
    Tests that non-TTY startup is explicitly refused and wrapper is not invoked.
    """
    with mock.patch("src.scripts.clean_release_tui.curses.wrapper") as curses_wrapper_mock:
        with mock.patch("sys.stdout.isatty", return_value=False):
            exit_code = main()

        curses_wrapper_mock.assert_not_called()

        assert exit_code == 2
        captured = capsys.readouterr()
        assert "TTY is required for TUI mode" in captured.err
        assert "Use CLI/API workflow instead" in captured.err


@patch("src.scripts.clean_release_tui.curses")
def test_tui_initial_render(mock_curses_module, mock_stdscr: MagicMock):
    """
    Simulates the initial rendering cycle of the TUI application to ensure
    titles, headers, footers and the READY state are drawn appropriately.
    """
    # Ensure constants match
    mock_curses_module.KEY_F10 = curses.KEY_F10
    mock_curses_module.KEY_F5 = curses.KEY_F5
    mock_curses_module.color_pair.side_effect = lambda x: x
    mock_curses_module.A_BOLD = 0

    app = CleanReleaseTUI(mock_stdscr)
    assert app.status == "READY"
    
    # We only want to run one loop iteration, so we mock getch to return F10
    mock_stdscr.getch.return_value = curses.KEY_F10
    
    app.loop()
    
    # Assert header was drawn
    addstr_calls = mock_stdscr.addstr.call_args_list
    assert any("Enterprise Clean Release Validator" in str(call) for call in addstr_calls)
    assert any("Candidate: [2026.03.03-rc1]" in str(call) for call in addstr_calls)
    
    # Assert checks list is shown
    assert any("Data Purity" in str(call) for call in addstr_calls)
    assert any("Internal Sources Only" in str(call) for call in addstr_calls)

    # Assert footer is shown
    assert any("F5 Run" in str(call) for call in addstr_calls)


@patch("src.scripts.clean_release_tui.curses")
def test_tui_run_checks_f5(mock_curses_module, mock_stdscr: MagicMock):
    """
    Simulates pressing F5 to transition into the RUNNING checks flow.
    """
    # Ensure constants match
    mock_curses_module.KEY_F10 = curses.KEY_F10
    mock_curses_module.KEY_F5 = curses.KEY_F5
    mock_curses_module.color_pair.side_effect = lambda x: x
    mock_curses_module.A_BOLD = 0

    app = CleanReleaseTUI(mock_stdscr)
    
    # getch sequence:
    # 1. First loop: F5 (triggers run_checks)
    # 2. Next call after run_checks: F10 to exit
    mock_stdscr.f5_pressed = False
    def side_effect():
        if not mock_stdscr.f5_pressed:
            mock_stdscr.f5_pressed = True
            return curses.KEY_F5
        return curses.KEY_F10
    
    mock_stdscr.getch.side_effect = side_effect
    
    with mock.patch("time.sleep", return_value=None):
        app.loop()
    
    # After F5 is pressed, status should be BLOCKED due to deliberate 'test-data' violation
    assert app.status == CheckFinalStatus.BLOCKED
    assert app.report_id is not None
    assert "CCR-" in app.report_id
    assert len(app.violations_list) > 0


@patch("src.scripts.clean_release_tui.curses")
def test_tui_exit_f10(mock_curses_module, mock_stdscr: MagicMock):
    """
    Simulates pressing F10 to exit the application immediately without running checks.
    """
    # Ensure constants match
    mock_curses_module.KEY_F10 = curses.KEY_F10
    
    app = CleanReleaseTUI(mock_stdscr)
    mock_stdscr.getch.return_value = curses.KEY_F10
    
    # loop() should return cleanly
    app.loop()
    
    assert app.status == "READY"


@patch("src.scripts.clean_release_tui.curses")
def test_tui_clear_history_f7(mock_curses_module, mock_stdscr: MagicMock):
    """
    Simulates pressing F7 to clear history.
    """
    mock_curses_module.KEY_F10 = curses.KEY_F10
    mock_curses_module.KEY_F7 = curses.KEY_F7
    mock_curses_module.color_pair.side_effect = lambda x: x
    mock_curses_module.A_BOLD = 0

    app = CleanReleaseTUI(mock_stdscr)
    app.status = CheckFinalStatus.BLOCKED
    app.report_id = "SOME-REPORT"
    
    # F7 then F10
    mock_stdscr.getch.side_effect = [curses.KEY_F7, curses.KEY_F10]
    
    app.loop()
    
    assert app.status == "READY"
    assert app.report_id is None
    assert len(app.checks_progress) == 0


# [/DEF:backend.tests.scripts.test_clean_release_tui:Module]
