"""
Logging-based output handler for the data-validation package.

The data-validation validators call ``context.output_handler.handle_message(...)``
throughout their execution.  This concrete implementation routes those messages
to Python's standard ``logging`` module and tracks pass/fail state so the
test-runner can inspect results after validation completes.
"""

from __future__ import annotations

import logging
from typing import Optional

import pandas as pd

from snowflake.snowflake_data_validation.utils.base_output_handler import (
    OutputHandlerBase,
    OutputMessageLevel,
)
from snowflake.snowflake_data_validation.utils.constants import ValidationLevel

from test_runner.common.models import MAX_ERROR_LENGTH, ValidationDifference, truncate


LOGGER = logging.getLogger("test_runner.validate")

_LEVEL_MAP: dict[OutputMessageLevel, int] = {
    OutputMessageLevel.DEBUG: logging.DEBUG,
    OutputMessageLevel.INFO: logging.INFO,
    OutputMessageLevel.WARNING: logging.WARNING,
    OutputMessageLevel.ERROR: logging.ERROR,
    OutputMessageLevel.SUCCESS: logging.INFO,
    OutputMessageLevel.FAILURE: logging.WARNING,
    OutputMessageLevel.SOURCE_RESULT: logging.DEBUG,
    OutputMessageLevel.TARGET_RESULT: logging.DEBUG,
}


class LoggingOutputHandler(OutputHandlerBase):
    """Route data-validation output messages to Python ``logging``.

    Also tracks pass/fail state per validation level so the test-runner
    can inspect results after ``SyncValidationExecutor`` completes.
    """

    def __init__(self) -> None:
        super().__init__(enable_console_output=False)
        self._has_failures = False
        self._failed_level: Optional[str] = None
        self._differences: list[ValidationDifference] = []

    @property
    def has_failures(self) -> bool:
        """Whether any validation level reported a failure."""
        return self._has_failures

    @property
    def failed_level(self) -> Optional[str]:
        """The first validation level that failed, or ``None``."""
        return self._failed_level

    @property
    def differences(self) -> list[ValidationDifference]:
        """Collected differences from failed validation levels."""
        return self._differences

    def handle_message(
        self,
        level: OutputMessageLevel,
        message: str = "",
        header: str = "",
        dataframe: pd.DataFrame = None,
    ) -> None:
        log_level = _LEVEL_MAP.get(level, logging.INFO)
        parts: list[str] = []
        if header:
            parts.append(header)
        if message:
            parts.append(message)
        text = " ".join(parts) if parts else "(no message)"
        LOGGER.log(log_level, text)

        if level == OutputMessageLevel.FAILURE:
            self._has_failures = True
            if self._failed_level is None:
                self._failed_level = self._infer_level(header, message)
            self._differences.append(ValidationDifference(
                level=self._failed_level or "",
                type="FAILURE",
                detail=truncate(text, MAX_ERROR_LENGTH),
            ))

    @staticmethod
    def _infer_level(header: str, message: str) -> Optional[str]:
        """Infer the validation level from the message content."""
        combined = (header + " " + message).lower()
        if "schema" in combined:
            return ValidationLevel.SCHEMA_VALIDATION.value
        if "metric" in combined:
            return ValidationLevel.METRICS_VALIDATION.value
        if "row" in combined:
            return ValidationLevel.ROW_VALIDATION.value
        return None
