"""Anki Connect API client."""

import requests
import logging
from typing import Dict, List, Any, Optional

logger = logging.getLogger(__name__)


class AnkiConnectError(Exception):
    """Exception raised for Anki Connect errors."""
    pass


class AnkiClient:
    """Client for interacting with Anki via AnkiConnect."""
    
    def __init__(self, host: str = "localhost", port: int = 8765):
        """Initialize Anki Connect client.
        
        Args:
            host: Anki Connect server host (default: localhost)
            port: Anki Connect server port (default: 8765)
        """
        self.url = f"http://{host}:{port}"
        self.version = 6
    
    def _request(self, action: str, **params) -> Dict[str, Any]:
        """Send a request to Anki Connect.
        
        Args:
            action: The action to perform
            **params: Parameters for the action
            
        Returns:
            The response from Anki Connect
            
        Raises:
            AnkiConnectError: If the request fails or returns an error
        """
        payload = {
            "action": action,
            "version": self.version,
            "params": params
        }
        
        try:
            response = requests.post(self.url, json=payload)
            response.raise_for_status()
        except requests.exceptions.ConnectionError as e:
            raise AnkiConnectError(f"Could not connect to Anki Connect at {self.url}. Is Anki running?") from e
        except requests.exceptions.RequestException as e:
            raise AnkiConnectError(f"Request failed: {e}") from e
        
        result = response.json()
        
        if result.get('error') is not None:
            raise AnkiConnectError(f"Anki Connect error: {result['error']}")
        
        return result.get('result')
    
    def get_all_note_ids(self) -> List[int]:
        """Get all note IDs from Anki.
        
        Returns:
            List of note IDs
        """
        logger.info("Fetching all note IDs from Anki")
        note_ids = self._request("findNotes", query="")
        logger.debug(f"Found {len(note_ids)} notes")
        return note_ids
    
    def get_notes_info(self, note_ids: List[int]) -> List[Dict[str, Any]]:
        """Get detailed information about notes.
        
        Args:
            note_ids: List of note IDs
            
        Returns:
            List of note info dictionaries
        """
        if not note_ids:
            return []
        
        logger.info(f"Fetching info for {len(note_ids)} notes")
        notes_info = self._request("notesInfo", notes=note_ids)
        return notes_info
    
    def get_model_field_names(self, model_name: str) -> List[str]:
        """Get field names for a note type (model).
        
        Args:
            model_name: Name of the note type
            
        Returns:
            List of field names
        """
        logger.debug(f"Fetching field names for model: {model_name}")
        models = self._request("modelNames")
        
        if model_name not in models:
            raise AnkiConnectError(f"Model '{model_name}' not found")
        
        field_names = self._request("modelFieldNames", modelName=model_name)
        return field_names
    
    def add_note(self, note_data: Dict[str, Any]) -> int:
        """Add a new note to Anki.
        
        Args:
            note_data: Note data with keys: 'deckName', 'modelName', 'fields', 'tags'
            
        Returns:
            The ID of the created note
        """
        logger.info(f"Adding note to deck: {note_data.get('deckName')}")
        note_id = self._request("addNote", note=note_data)
        logger.debug(f"Created note with ID: {note_id}")
        return note_id
    
    def update_note(self, note_id: int, fields: Dict[str, str], tags: List[str]) -> None:
        """Update an existing note.
        
        Args:
            note_id: ID of the note to update
            fields: Dictionary of field_name -> field_value
            tags: List of tags
        """
        logger.info(f"Updating note {note_id}")
        
        note = {
            "id": note_id,
            "fields": fields,
            "tags": tags
        }
        
        self._request("updateNote", note=note)
        logger.debug(f"Updated note {note_id}")
    
    def note_exists(self, note_id: int) -> bool:
        """Check if a note exists.
        
        Args:
            note_id: ID of the note to check
            
        Returns:
            True if note exists, False otherwise
        """
        try:
            result = self._request("notesInfo", notes=[note_id])
            return result and len(result) > 0
        except AnkiConnectError:
            return False
    
    def get_decks(self) -> Dict[str, Dict[str, Any]]:
        """Get all deck names and info.
        
        Returns:
            Dictionary of deck name -> deck info
        """
        logger.debug("Fetching deck list")
        decks = self._request("deckNames")
        return decks if isinstance(decks, dict) else {d: {} for d in decks}
