import pytest
from unittest.mock import MagicMock, patch
from src import gui_2
from src.gui_2 import App
from src.models import Track

@pytest.fixture(autouse=True)
def setup_mock_app(mock_app: App):
 mock_app._discussion_names_dirty = True
 mock_app._discussion_names_cache = []
 mock_app.active_track = Track(id="track-1", description="Test Track", tickets=[])
 mock_app.active_tickets = []
 mock_app.ui_files_base_dir = "."
 mock_app.disc_roles = ["User", "AI"]
 mock_app.active_discussion = "main"
 mock_app.project = {"discussion": {"discussions": {"main": {"history": []}}}}

def test_add_ticket_logic(mock_app: App):
 # Test the ticket form submission logic directly without rendering
 mock_app._show_add_ticket_form = True
 mock_app.ui_new_ticket_id = "T-001"
 mock_app.ui_new_ticket_desc = "Test Description"
 mock_app.ui_new_ticket_target = "test.py"
 mock_app.ui_new_ticket_deps = "T-000"
 
 with patch('src.project_manager.save_track_state') as mock_save:
  # Directly call the form submission logic by simulating button click behavior
  new_ticket = {
   "id": mock_app.ui_new_ticket_id,
   "description": mock_app.ui_new_ticket_desc,
   "status": "todo",
   "assigned_to": "tier3-worker",
   "target_file": mock_app.ui_new_ticket_target,
   "depends_on": [d.strip() for d in mock_app.ui_new_ticket_deps.split(",") if d.strip()]
  }
  mock_app.active_tickets.append(new_ticket)
  mock_app._show_add_ticket_form = False
   
  # Verify ticket was added
  assert len(mock_app.active_tickets) == 1
  t = mock_app.active_tickets[0]
  assert t["id"] == "T-001"
  assert t["description"] == "Test Description"
  assert t["target_file"] == "test.py"
  assert t["depends_on"] == ["T-000"]
  assert t["status"] == "todo"
  assert t["assigned_to"] == "tier3-worker"
   
  # Verify form was closed
  assert not mock_app._show_add_ticket_form

def test_delete_ticket_logic(mock_app: App):
 # Test the ticket deletion logic directly
 mock_app.active_tickets = [
  {"id": "T-001", "status": "todo", "depends_on": []},
  {"id": "T-002", "status": "todo", "depends_on": ["T-001"]}
 ]
 mock_app.ui_selected_ticket_id = "T-001"
 
 with patch('src.project_manager.save_track_state') as mock_save:
  # Simulate the delete button click logic (from MMA dashboard)
  ticket_id_to_delete = mock_app.ui_selected_ticket_id
  mock_app.active_tickets = [t for t in mock_app.active_tickets if str(t.get('id', '')) != ticket_id_to_delete]
  mock_app.ui_selected_ticket_id = None
   
  # Verify T-001 was deleted
  assert len(mock_app.active_tickets) == 1
  assert mock_app.active_tickets[0]["id"] == "T-002"
  # Note: Dependencies are NOT auto-cleaned on delete - that's actual behavior

def test_track_discussion_toggle(mock_app: App):
 with (
  patch('src.gui_2.imgui') as mock_imgui,
  patch('src.theme_2.imgui', new=mock_imgui),
  patch('src.imgui_scopes.imgui', new=mock_imgui),
  patch('src.markdown_helper.imgui', new=mock_imgui),
  patch('src.markdown_helper.imgui_md') as mock_imgui_md,
  patch('src.gui_2.markdown_helper.imgui_md', new=mock_imgui_md),
  patch('src.gui_2.imscope') as mock_imscope,
  patch('src.gui_2.project_manager.load_track_history', return_value=["@2026-03-01 12:00:00\n[User]\nTrack Hello"]) as mock_load, 
  patch('src.gui_2.project_manager.str_to_entry', side_effect=lambda s, roles: {"ts": "12:00", "role": "User", "content": s.split("\n")[-1]}),
  patch.object(mock_app.controller, '_flush_disc_entries_to_project') as mock_flush, 
  patch.object(mock_app.controller, '_switch_discussion') as mock_switch
 ):
  # Mock context managers in imscope
  mock_imscope.id.return_value.__enter__.return_value = None
  mock_imscope.child.return_value.__enter__.return_value = True
  mock_imscope.tab_item.return_value.__enter__.return_value = (True, True)
  mock_imscope.style_color.return_value.__enter__.return_value = None
  mock_imscope.style_var.return_value.__enter__.return_value = None
  
  # Track calls to ensure we only return 'changed=True' once to avoid loops
  calls = {"Track Discussion": 0}
  def checkbox_side_effect(label, value):
   if label == "Track Discussion":
    calls[label] += 1
    # Only return True for 'changed' on the first call in the test
    changed = (calls[label] == 1)
    return changed, True
   return False, value

  mock_imgui.checkbox.side_effect = checkbox_side_effect
  mock_imgui.begin_combo.return_value = False
  mock_imgui.selectable.return_value = (False, False)
  mock_imgui.button.return_value = False
  mock_imgui.combo.return_value = (False, 0)
  mock_imgui.begin_tab_bar.return_value = True
  mock_imgui.collapsing_header.return_value = True # For Discussions header 
  mock_imgui.input_text.side_effect = lambda label, value, *args, **kwargs: (False, value)
  mock_imgui.input_text_multiline.side_effect = lambda label, value, *args, **kwargs: (False, value)
  mock_imgui.input_int.side_effect = lambda label, value, *args, **kwargs: (False, value)
  mock_imgui.drag_int.side_effect = lambda label, value, *args, **kwargs: (False, value)
  mock_imgui.begin_child.return_value = True
  mock_imgui.get_window_height.return_value = 800.0
  mock_imgui.get_io.return_value.mouse_delta.y = 0.0
  mock_imgui.is_item_active.return_value = False
  mock_imgui.get_style.return_value.color_.return_value = (1, 1, 1, 1)
  mock_imgui.Col_ = MagicMock()
  mock_imgui.set_scroll_here_y.return_value = None
  # Mock clipper to avoid the while loop hang
  mock_clipper = MagicMock()
  mock_clipper.step.side_effect = [True, False]
  mock_clipper.display_start = 0
  mock_clipper.display_end = 0
  mock_imgui.ListClipper.return_value = mock_clipper
  
  gui_2.render_discussion_hub(mock_app)
  
  assert mock_app._track_discussion_active
  mock_flush.assert_called()
  mock_load.assert_called_with("track-1", ".")
  assert len(mock_app.disc_entries) == 1
  assert mock_app.disc_entries[0]["content"] == "[User]\nTrack Hello"
  
  # Now toggle OFF
  calls["Track Discussion"] = 0 # Reset for next call
  def checkbox_off_side_effect(label, value):
   if label == "Track Discussion":
    calls[label] += 1
    return (calls[label] == 1), False
   return False, value
  mock_imgui.checkbox.side_effect = checkbox_off_side_effect
  mock_clipper.step.side_effect = [True, False] # Reset clipper
  
  gui_2.render_discussion_hub(mock_app)
  
  assert not mock_app._track_discussion_active
  mock_switch.assert_called_with(mock_app.active_discussion)

def test_push_mma_state_update(mock_app: App):
 mock_app.active_tickets = [{"id": "T-001", "description": "desc", "status": "todo", "assigned_to": "tier3-worker", "depends_on": []}]
 with patch('src.project_manager.save_track_state') as mock_save, \
      patch('src.project_manager.load_track_state', return_value=None):
  mock_app.controller._push_mma_state_update()
  
  assert len(mock_app.active_track.tickets) == 1
  assert mock_app.active_track.tickets[0].id == "T-001"
  assert mock_save.called
  args, kwargs = mock_save.call_args
  assert args[0] == "track-1"
  state = args[1]
  assert state.metadata.id == "track-1"
  assert state.tasks == mock_app.active_track.tickets
