from typing import Dict, List, Optional
from .column import Column
import pandas as pd
from .exceptions import ColumnValidationError, ValidationError
from .validators import (
    validate_type,
    validate_not_null,
    validate_unique,
    validate_min,
    validate_max,
    validate_regex,
)

class Schema:
    def __init__(self, version: str, columns: Optional[List[Column]] = None):
        self.version = version
        self.columns: Dict[str, Column] = {}
        for c in (columns or []):
            self.columns[c.name] = c

    def add_column(self, column: Column):
        self.columns[column.name] = column

    def get_column(self, name: str) -> Optional[Column]:
        return self.columns.get(name)

    def validate(self, df: pd.DataFrame, raise_on_error: bool = True) -> Dict[str, Dict]:
        """
        Validate DataFrame against the schema.
        Returns a report dict:
            { "ok": bool, "errors": { column_name: { "reason": str, "sample": [...] } } }
        If raise_on_error is True and any error occurs, raises ValidationError with message and details.
        """
        errors = {}
        # ensure df is a DataFrame
        if not isinstance(df, pd.DataFrame):
            raise ValidationError("Input is not a pandas DataFrame.")

        for col_name, col in self.columns.items():
            # get series (None if missing)
            series = df[col_name] if col_name in df.columns else None
            try:
                # type check first
                validate_type(series, col.dtype, col_name)
                # null check
                if col.not_null:
                    validate_not_null(series, col_name)
                # unique
                if col.unique:
                    validate_unique(series, col_name)
                # bounds
                if col.min_value is not None:
                    validate_min(series, col.min_value, col_name)
                if col.max_value is not None:
                    validate_max(series, col.max_value, col_name)
                # regex
                if col.regex is not None:
                    validate_regex(series, col.regex, col_name)
            except ColumnValidationError as e:
                errors[col_name] = {"reason": e.reason, "sample": e.sample}

        ok = len(errors) == 0
        report = {"ok": ok, "errors": errors, "version": self.version}
        if not ok and raise_on_error:
            # Build explanatory message
            parts = []
            for cn, info in errors.items():
                parts.append(f"{cn}: {info['reason']} (sample: {info.get('sample', [])})")
            raise ValidationError("Schema validation failed: " + "; ".join(parts))
        return report
