class ConfigValidator:
    VALID_STRATEGY_TYPES = {"random", "stratified", "stratified-proportional"}

    def __init__(self, config: dict):
        self.config = config

    def validate(self):
        self.validate_customer_name()
        self.validate_data_sources()
        self.validate_filters()
        self.validate_sampling_strategy()
        self.validate_output()
        self.validate_alerting()

    def validate_customer_name(self):
        if "customer_name" not in self.config:
            raise ValueError("Missing 'customer_name' in config.")
        if not isinstance(self.config["customer_name"], str):
            raise ValueError("'customer_name' must be a string.")

    def validate_data_sources(self):
        if "data_sources" not in self.config:
            raise ValueError("Missing 'data_sources' in config.")
        
        for source in ["matches", "catalog"]:
            if source not in self.config["data_sources"]:
                raise ValueError(f"Missing '{source}' in data_sources.")
            
            source_conf = self.config["data_sources"][source]
            if "sql_file" not in source_conf:
                raise ValueError(f"Missing 'sql_file' in {source} data source.")
            if "connection" not in source_conf:
                raise ValueError(f"Missing 'connection' details in {source} data source.")
            if "secret_name" not in source_conf["connection"]:
                raise ValueError(f"Missing 'secret_name' in {source} connection config.")

    def validate_filters(self):
        filters = self.config.get("filters", {})
        if not isinstance(filters, dict):
            raise ValueError("'filters' section must be a dictionary if provided.")

    def validate_sampling_strategy(self):
        if "sampling_strategy" not in self.config:
            raise ValueError("Missing 'sampling_strategy' in config.")
        
        strategy = self.config["sampling_strategy"]
        if "type" not in strategy:
            raise ValueError("sampling_strategy must include a 'type' field.")
        if strategy["type"] not in self.VALID_STRATEGY_TYPES:
            raise ValueError(f"sampling_strategy.type must be one of {self.VALID_STRATEGY_TYPES}")

        # Strategy-specific checks
        if strategy["type"] == "random":
            if "sample_size" not in strategy:
                raise ValueError("random sampling_strategy must have 'sample_size' defined.")

        elif strategy["type"] == "stratified":
            if "by" not in strategy:
                raise ValueError("stratified sampling_strategy must have 'by' field.")
            # 'ratio' is optional — default to 10/group in Sampler
            if "ratio" in strategy:
                if not isinstance(strategy["ratio"], (dict, int)):
                    raise ValueError("'ratio' must be an int or dict in stratified strategy.")

        elif strategy["type"] == "stratified-proportional":
            if "by" not in strategy:
                raise ValueError("stratified-proportional sampling_strategy must have 'by' field.")
            # 'proportion' is optional — default to equal weight in Sampler
            if "proportion" in strategy:
                if not isinstance(strategy["proportion"], (dict, float, int)):
                    raise ValueError("'proportion' must be a float or dict in stratified-proportional strategy.")

    def validate_output(self):
        if "output" not in self.config:
            raise ValueError("Missing 'output' section in config.")
        
        output = self.config["output"]
        if "bucket" not in output:
            raise ValueError("output must include 'bucket'.")
        if "prefix" not in output:
            raise ValueError("output must include 'prefix'.")
        if "file_format" not in output:
            raise ValueError("output must include 'file_format' (csv or parquet).")

    def validate_alerting(self):
        alerting = self.config.get("alerting", {})
        if alerting and not isinstance(alerting, dict):
            raise ValueError("'alerting' must be a dictionary if provided.")
        
        if alerting:
            if "slack_webhook_url" not in alerting and "email_list" not in alerting:
                raise ValueError("alerting must have either 'slack_webhook_url' or 'email_list'.")
