"""Elasticsearch backend for logging to Elasticsearch."""

from dataclasses import dataclass
from typing import Any

from ..core.backend import BackendConfig, LoggingBackend, LogRecord
from ..formatters import JSONFormatter, LogFormatter

try:
    from elasticsearch import AsyncElasticsearch
    ELASTICSEARCH_AVAILABLE = True
except ImportError:
    ELASTICSEARCH_AVAILABLE = False
    AsyncElasticsearch = None


@dataclass
class ElasticsearchBackendConfig(BackendConfig):
    """Configuration for Elasticsearch backend."""

    enabled: bool = True
    timeout: float = 10.0
    retry_attempts: int = 3
    retry_delay: float = 1.0
    host: str = "localhost"
    port: int = 9200
    index_prefix: str = "logs"
    username: str | None = None
    password: str | None = None
    use_ssl: bool = False
    verify_certs: bool = True
    batch_size: int = 100


class ElasticsearchBackend(LoggingBackend):
    """Backend that sends logs to Elasticsearch.
    
    Note: This is a placeholder implementation. In a production environment,
    you would install and use the elasticsearch-async library or similar.
    """

    def __init__(
        self,
        name: str = "elasticsearch",
        config: ElasticsearchBackendConfig | None = None,
        formatter: LogFormatter | None = None,
        **kwargs: Any
    ) -> None:
        """Initialize Elasticsearch backend.
        
        Args:
            name: Backend name
            config: Backend configuration
            formatter: Log formatter to use (defaults to JSON)
            **kwargs: Additional configuration
        """
        self.config = config or ElasticsearchBackendConfig()
        super().__init__(name, self.config, **kwargs)

        self.formatter = formatter or JSONFormatter()
        self._client = None

        # For backward compatibility with tests
        if 'hosts' in kwargs:
            self.hosts = kwargs['hosts']
        else:
            self.hosts = [f"{self.config.host}:{self.config.port}"]
        self.index_prefix = kwargs.get('index_prefix', self.config.index_prefix)
        self.timeout = kwargs.get('timeout', self.config.timeout)

    async def connect(self) -> bool:
        """Connect to Elasticsearch cluster."""
        if not ELASTICSEARCH_AVAILABLE:
            raise ImportError(
                "Elasticsearch backend requires 'elasticsearch[async]' package. "
                "Install with: pip install elasticsearch[async]"
            )

        # Build connection parameters - use URL format for Elasticsearch 8.x
        # Check if host already includes scheme
        if self.config.host.startswith(('http://', 'https://')):
            host_url = self.config.host
        else:
            scheme = "https" if self.config.use_ssl else "http"
            host_url = f"{scheme}://{self.config.host}:{self.config.port}"

        # Set compatibility headers for ES client v9 with ES server v8
        headers = {
            "accept": "application/json",
            "content-type": "application/json"
        }

        kwargs = {
            "hosts": [host_url],
            "request_timeout": self.config.timeout,
            "headers": headers,
        }

        # Add authentication if provided
        if self.config.username and self.config.password:
            kwargs["basic_auth"] = (self.config.username, self.config.password)

        # SSL configuration
        if self.config.use_ssl:
            kwargs["verify_certs"] = self.config.verify_certs

        try:
            self._client = AsyncElasticsearch(**kwargs)

            # Test the connection
            await self._client.ping()
            self._connected = True
            return True

        except Exception as e:
            self._client = None
            raise ConnectionError(f"Failed to connect to Elasticsearch: {e}") from e

    async def disconnect(self) -> None:
        """Disconnect from Elasticsearch."""
        if self._client:
            try:
                await self._client.close()
            except Exception:
                # Ignore errors during disconnect
                pass
            finally:
                self._client = None
        self._connected = False

    async def send_log(self, record: LogRecord) -> bool:
        """Send log record to Elasticsearch.
        
        Args:
            record: The log record to send
            
        Returns:
            True if successfully indexed, False otherwise
        """
        if not self._client:
            return False

        try:
            # Format record as dictionary for Elasticsearch
            if hasattr(self.formatter, 'format_dict'):
                doc = self.formatter.format_dict(record)
            else:
                # Fallback to parsing JSON string
                import json
                doc = json.loads(self.formatter.format(record))

            # Generate index name with date
            index_name = f"{self.config.index_prefix}-{record.timestamp.strftime('%Y.%m.%d')}"

            # Index the document
            response = await self._client.index(
                index=index_name,
                document=doc
            )

            # Check if indexing was successful
            return response.get("result") in ["created", "updated"]

        except Exception as e:
            # Log the error but don't raise to avoid breaking other backends
            import logging
            logging.getLogger(__name__).error(
                f"Failed to index log to Elasticsearch: {e}"
            )
            return False

    async def send_logs_batch(self, records: list[LogRecord]) -> list[bool]:
        """Send multiple log records as a batch to Elasticsearch.
        
        Args:
            records: List of log records to send
            
        Returns:
            List of success/failure status for each record
        """
        if not self._client or not records:
            return [False] * len(records)

        try:
            # Prepare bulk operations
            operations = []

            for record in records:
                # Format record as dictionary
                if hasattr(self.formatter, 'format_dict'):
                    doc = self.formatter.format_dict(record)
                else:
                    import json
                    doc = json.loads(self.formatter.format(record))

                # Generate index name with date
                index_name = f"{self.config.index_prefix}-{record.timestamp.strftime('%Y.%m.%d')}"

                # Add index operation
                operations.append({"index": {"_index": index_name}})
                operations.append(doc)

            # Execute bulk operation
            response = await self._client.bulk(operations=operations)

            # Parse bulk response
            results = []
            for item in response.get("items", []):
                if "index" in item:
                    status = item["index"].get("status")
                    results.append(status in [200, 201])
                else:
                    results.append(False)

            return results

        except Exception as e:
            import logging
            logging.getLogger(__name__).error(
                f"Failed to bulk index logs to Elasticsearch: {e}"
            )
            return [False] * len(records)

    async def health_check(self) -> bool:
        """Check Elasticsearch cluster health.
        
        Returns:
            True if cluster is healthy, False otherwise
        """
        if not self._client:
            return False

        try:
            # Ping the cluster
            return await self._client.ping()

        except Exception:
            return False
