"""
Session history management for Promptheus.
Handles saving and retrieving prompt refinement history.
"""

import json
import logging
import os
import sys
from dataclasses import dataclass, asdict
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple

from promptheus.utils import sanitize_error_message

logger = logging.getLogger(__name__)


def get_default_history_dir() -> Path:
    """
    Get the default history directory based on platform.

    Returns:
        Path: Platform-appropriate history directory
            - Unix/Linux/Mac: ~/.promptheus
            - Windows: %APPDATA%/promptheus
    """
    if sys.platform == "win32":
        # On Windows, use AppData/Roaming
        appdata = os.environ.get("APPDATA")
        if appdata:
            return Path(appdata) / "promptheus"
        # Fallback to home directory if APPDATA not set
        return Path.home() / "promptheus"
    else:
        # Unix-like systems: use hidden directory in home
        return Path.home() / ".promptheus"


@dataclass
class HistoryEntry:
    """Represents a single history entry."""
    timestamp: str
    original_prompt: str
    refined_prompt: str
    task_type: Optional[str] = None
    provider: Optional[str] = None
    model: Optional[str] = None

    def to_dict(self):
        """Convert to dictionary."""
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict) -> "HistoryEntry":
        """Create from dictionary."""
        return cls(**data)


class PromptHistory:
    """Manages prompt history storage and retrieval."""

    def __init__(self, history_dir: Optional[Path] = None, config=None):
        """
        Initialize history manager.

        Args:
            history_dir: Directory to store history files. Defaults to platform-specific location:
                - Unix/Linux/Mac: ~/.promptheus
                - Windows: %APPDATA%/promptheus
            config: Configuration object to check if history is enabled
        """
        if history_dir is None:
            history_dir = get_default_history_dir()

        self.history_dir = history_dir
        self.history_file = self.history_dir / "history.jsonl"  # JSONL format
        self.prompt_history_file = self.history_dir / "prompt_history.txt"
        self.config = config

        # Create directory if it doesn't exist
        self._ensure_directory()

    @property
    def enabled(self) -> bool:
        """Check if history persistence is enabled."""
        if self.config is None:
            # Fallback to enabled if no config provided (backward compatibility)
            return True
        return self.config.history_enabled

    def _ensure_directory(self) -> None:
        """Create history directory if it doesn't exist."""
        try:
            self.history_dir.mkdir(parents=True, exist_ok=True)
        except PermissionError:
            logger.error(
                "Permission denied when creating history directory at %s",
                self.history_dir,
            )
        except OSError as exc:
            logger.error(
                "Failed to create history directory (%s): %s",
                self.history_dir,
                sanitize_error_message(str(exc)),
            )

    def save_entry(
        self,
        original_prompt: str,
        refined_prompt: str,
        task_type: Optional[str] = None,
        provider: Optional[str] = None,
        model: Optional[str] = None
    ) -> None:
        """
        Save a history entry in O(1) time using append-only JSONL format.

        Args:
            original_prompt: The original user prompt
            refined_prompt: The final refined/accepted prompt
            task_type: Type of task (analysis, generation, etc.)
            provider: The provider used for the prompt
            model: The model used for the prompt
        """
        # Check if history is enabled
        if not self.enabled:
            logger.debug("History persistence is disabled - skipping save")
            return

        entry = HistoryEntry(
            timestamp=datetime.now().isoformat(),
            original_prompt=original_prompt,
            refined_prompt=refined_prompt,
            task_type=task_type,
            provider=provider,
            model=model
        )

        try:
            # Append entry to JSONL file (O(1) operation)
            # Use append mode which should be atomic on most systems
            with open(self.history_file, 'a', encoding='utf-8') as f:
                json.dump(entry.to_dict(), f)
                f.write('\n')
                f.flush()  # Ensure data is written immediately

            # Also save to prompt history file for arrow key navigation
            self._append_to_prompt_history(original_prompt)

            logger.debug(f"Saved history entry: {entry.timestamp}")

        except Exception as exc:
            logger.error("Failed to save history entry: %s", sanitize_error_message(str(exc)))

    def _load_history(self) -> List[HistoryEntry]:
        """Load history from JSONL file."""
        if not self.history_file.exists():
            return []

        try:
            history = []
            with open(self.history_file, 'r', encoding='utf-8') as f:
                for line_num, line in enumerate(f, 1):
                    line = line.strip()
                    if not line:
                        continue
                    try:
                        entry_data = json.loads(line)
                        history.append(HistoryEntry.from_dict(entry_data))
                    except json.JSONDecodeError as exc:
                        logger.warning(f"Failed to parse history line {line_num}: {exc}")
                        continue
            return history
        except OSError as exc:
            logger.error(
                "Failed to read history file (%s): %s",
                self.history_file,
                sanitize_error_message(str(exc)),
            )
            return []

    def _append_to_prompt_history(self, prompt: str) -> None:
        """Append a prompt to the prompt history file for arrow key navigation."""
        # Check if history is enabled
        if not self.enabled:
            logger.debug("History persistence is disabled - skipping prompt history append")
            return

        try:
            with open(self.prompt_history_file, 'a', encoding='utf-8') as f:
                # Write prompt on a single line with proper escaping
                # Escape control characters that would break the one-line-per-entry format
                sanitized = prompt.replace('\\', '\\\\').replace('\r', '\\r').replace('\n', '\\n')
                f.write(f"{sanitized}\n")
        except OSError as exc:
            logger.error(
                "Failed to append to prompt history file (%s): %s",
                self.prompt_history_file,
                sanitize_error_message(str(exc)),
            )

    def get_recent(self, limit: int = 20) -> List[HistoryEntry]:
        """
        Get recent history entries.

        Args:
            limit: Maximum number of entries to return

        Returns:
            List of history entries, most recent first
        """
        history = self._load_history()
        return list(reversed(history[-limit:]))

    def get_all(self) -> List[HistoryEntry]:
        """Get all history entries, most recent first."""
        history = self._load_history()
        return list(reversed(history))

    def get_total_count(self) -> int:
        """
        Get the total number of history entries.

        Returns:
            Total number of history entries
        """
        history = self._load_history()
        return len(history)

    def get_paginated(self, offset: int = 0, limit: int = 50) -> Tuple[List[HistoryEntry], int]:
        """
        Get paginated history entries.

        Args:
            offset: Number of entries to skip
            limit: Maximum number of entries to return

        Returns:
            Tuple of (list of entries, total count)
        """
        all_entries = self.get_all()
        total_count = len(all_entries)
        paginated_entries = all_entries[offset:offset+limit]
        return paginated_entries, total_count

    def get_all(self) -> List[HistoryEntry]:
        """Get all history entries, most recent first."""
        history = self._load_history()
        return list(reversed(history))

    def get_by_index(self, index: int) -> Optional[HistoryEntry]:
        """
        Get a history entry by index (1-based, from most recent).

        Args:
            index: 1-based index (1 = most recent)

        Returns:
            History entry or None if index is out of range
        """
        if not self.history_file.exists():
            return None

        try:
            # For JSONL format, we can read line by line to find the target index
            # We need to read from the beginning and keep track of line count
            current_index = 0
            lines = []

            with open(self.history_file, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue
                    current_index += 1
                    lines.append(line)

                    # Keep only the most recent entries up to the target index
                    if len(lines) > index:
                        lines.pop(0)  # Remove oldest line to maintain size

            if 1 <= index <= len(lines):
                target_line = lines[-index]  # Get the line from end (most recent)
                entry_data = json.loads(target_line)
                return HistoryEntry.from_dict(entry_data)

            return None
        except (OSError, json.JSONDecodeError, IndexError) as exc:
            logger.warning(f"Failed to get history entry by index {index}: {exc}")
            return None

    def delete_by_timestamp(self, timestamp: str) -> bool:
        """
        Delete a history entry by timestamp.

        Args:
            timestamp: ISO format timestamp of the entry to delete

        Returns:
            True if entry was deleted, False if not found
        """
        if not self.history_file.exists():
            return False

        try:
            # Load all entries
            entries = self._load_history()

            # Filter out the entry to delete
            filtered_entries = [e for e in entries if e.timestamp != timestamp]

            # Check if anything was removed
            if len(filtered_entries) == len(entries):
                return False  # Entry not found

            # Rewrite the file without the deleted entry
            with open(self.history_file, 'w', encoding='utf-8') as f:
                for entry in filtered_entries:
                    json.dump(entry.to_dict(), f)
                    f.write('\n')

            logger.info(f"Deleted history entry: {timestamp}")
            return True

        except OSError as exc:
            logger.error(
                "Failed to delete history entry: %s",
                sanitize_error_message(str(exc)),
            )
            return False

    def clear(self) -> None:
        """Clear all history."""
        try:
            if self.history_file.exists():
                self.history_file.unlink()
            if self.prompt_history_file.exists():
                self.prompt_history_file.unlink()
            logger.info("History cleared")
        except OSError as exc:
            logger.error(
                "Failed to clear history files: %s",
                sanitize_error_message(str(exc)),
            )

    def get_prompt_history_file(self) -> Path:
        """Get the path to the prompt history file for arrow key navigation."""
        return self.prompt_history_file


# Global instance
_history_instance: Optional[PromptHistory] = None


def get_history(config=None) -> PromptHistory:
    """
    Get the global history instance.

    Args:
        config: Optional configuration object to check history settings

    Returns:
        PromptHistory instance with config applied if provided
    """
    global _history_instance
    if _history_instance is None or config is not None:
        _history_instance = PromptHistory(config=config)
    return _history_instance
