import time
import typing

from dataclasses import dataclass, field


@dataclass
class UISnapshot:
 """Capture of restorable UI state."""
 ai_input:                str
 project_system_prompt:   str
 global_system_prompt:    str
 base_system_prompt:      str
 use_default_base_prompt: bool
 temperature:             float
 top_p:                   float
 max_tokens:              int
 auto_add_history:        bool
 disc_entries:            list[dict]
 files:                   list[dict]
 context_files:           list[dict]
 screenshots:             list[str]

 def to_dict(self) -> dict:
  """
  [C: src/models.py:ContextPreset.to_dict, src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
  """
  return {
   "ai_input":                self.ai_input,
   "project_system_prompt":   self.project_system_prompt,
   "global_system_prompt":    self.global_system_prompt,
   "base_system_prompt":      self.base_system_prompt,
   "use_default_base_prompt": self.use_default_base_prompt,
   "temperature":             self.temperature,
   "top_p":                   self.top_p,
   "max_tokens":              self.max_tokens,
   "auto_add_history":        self.auto_add_history,
   "disc_entries":            self.disc_entries,
   "files":                   self.files,
   "context_files":           self.context_files,
   "screenshots":             self.screenshots
  }

 @classmethod
 def from_dict(cls, data: dict) -> "UISnapshot":
  """
    [C: src/models.py:ContextPreset.from_dict, src/models.py:ExternalEditorConfig.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:load_mcp_config, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_from_dict_legacy, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_deserialization_with_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
  """
  return cls(
   ai_input                = data.get("ai_input", ""),
   project_system_prompt   = data.get("project_system_prompt", ""),
   global_system_prompt    = data.get("global_system_prompt", ""),
   base_system_prompt      = data.get("base_system_prompt", ""),
   use_default_base_prompt = data.get("use_default_base_prompt", True),
   temperature             = data.get("temperature", 0.0),
   top_p                   = data.get("top_p", 1.0),
   max_tokens              = data.get("max_tokens", 4096),
   auto_add_history        = data.get("auto_add_history", False),
   disc_entries            = data.get("disc_entries", []),
   files                   = data.get("files", []),
   context_files           = data.get("context_files", []),
   screenshots             = data.get("screenshots", [])
  )

@dataclass
class HistoryEntry:
 state:       typing.Any
 description: str
 timestamp:   float = field(default_factory=lambda: time.time())

class HistoryManager:
 def __init__(self, max_capacity: int = 100):
  """
  [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
  """
  self.max_capacity = max_capacity
  self._undo_stack: typing.List[HistoryEntry] = []
  self._redo_stack: typing.List[HistoryEntry] = []

 def push(self, state: typing.Any, description: str) -> None:
  """
  Pushes a new state to the undo stack and clears the redo stack.
  If the undo stack exceeds max_capacity, the oldest state is removed.
  [C: tests/test_history.py:test_jump_to_undo, tests/test_history.py:test_max_capacity, tests/test_history.py:test_push_state, tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions, tests/test_history_manager.py:TestHistoryManager.test_jump_to_undo, tests/test_history_manager.py:TestHistoryManager.test_push_and_undo, tests/test_history_manager.py:TestHistoryManager.test_push_clears_redo_stack, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo]
  """
  entry = HistoryEntry(state=state, description=description)
  self._undo_stack.append(entry)
  self._redo_stack.clear()
  if len(self._undo_stack) > self.max_capacity:
   self._undo_stack.pop(0)

 def undo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
  """
  Undoes the last action by moving the current_state to the redo stack
  and returning the top of the undo stack.
  [C: tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_push_and_undo, tests/test_history_manager.py:TestHistoryManager.test_push_clears_redo_stack, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo, tests/test_history_manager.py:TestHistoryManager.test_undo_no_history_returns_none]
  """
  if not self._undo_stack: return None
  redo_entry = HistoryEntry(state=current_state, description=current_description)
  self._redo_stack.append(redo_entry)
  return self._undo_stack.pop()

 def redo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
  """
  Redoes the last undone action by moving the current_state to the undo stack
  and returning the top of the redo stack.
  [C: tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_redo_no_history_returns_none, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo]
  """
  if not self._redo_stack: return None
  undo_entry = HistoryEntry(state=current_state, description=current_description)
  self._undo_stack.append(undo_entry)
  return self._redo_stack.pop()

 @property
 def can_undo(self) -> bool: return len(self._undo_stack) > 0
 @property
 def can_redo(self) -> bool: return len(self._redo_stack) > 0

 def get_history(self) -> typing.List[typing.Dict[str, typing.Any]]:
  """
  Returns a list of descriptions and timestamps for the undo stack.
  [C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions]
  """
  return [
   {"description": e.description, "timestamp": e.timestamp}
   for e in self._undo_stack
  ]

 def jump_to_undo(self, index: int, current_state: typing.Any, current_description: str = "Before Jump") -> typing.Optional[HistoryEntry]:
  """
  Jumps to a specific state in the undo stack by moving subsequent states
  and the current_state to the redo stack.
  [C: tests/test_history.py:test_jump_to_undo, tests/test_history_manager.py:TestHistoryManager.test_jump_to_undo]
  """
  if index < 0 or index >= len(self._undo_stack): return None
  # Move current state to redo
  self._redo_stack.append(HistoryEntry(state=current_state, description=current_description))
  # Move states between index and top of undo to redo
  while len(self._undo_stack) > index + 1:
   self._redo_stack.append(self._undo_stack.pop())
  return self._undo_stack.pop()
