import sys
import json
from datetime import datetime
from config_validator import ConfigValidator
from logger_setup import setup_logger
from alerting_handler import AlertingHandler
from data_loader import DataLoader
from filter_engine import FilterEngine
from sampler import Sampler
from data_writer import DataWriter
from utils import calculate_config_hash

def load_config(config_path):
    with open(config_path, "r") as f:
        return json.load(f)

def main(config_path, dry_run=False):
    logger = setup_logger()

    try:
        logger.info(f"Loading config from {config_path}")
        tenant_config = load_config(config_path)
        
        # Validate Config
        validator = ConfigValidator(tenant_config)
        validator.validate()
        logger.info("Config validation successful.")

        # Setup Alerting
        alert_handler = AlertingHandler(tenant_config.get("alerting", {}))

        customer_name = tenant_config["customer_name"]
        logger.info(f"Starting Recall Audit Sampling for tenant: {customer_name}")

        # Load Data
        data_loader = DataLoader(tenant_config["data_sources"]["matches"], tenant_config["data_sources"]["catalog"] , customer_name)
        matches_df, catalog_df = data_loader.load()

        logger.info(f"Loaded matches records: {len(matches_df)}")
        logger.info(f"Loaded catalog records: {len(catalog_df)}")

        # Filter Data
        filter_engine = FilterEngine(tenant_config)
        filtered_matches_df, filtered_catalog_df = filter_engine.apply(matches_df, catalog_df)

        logger.info(f"Post-filter matches: {len(filtered_matches_df)}")
        logger.info(f"Post-filter catalog: {len(filtered_catalog_df)}")

        # Recall candidates = catalog SKUs not found in matches
        matched_base_skus = filtered_matches_df['base_sku'].unique()
        recall_candidates_df = filtered_catalog_df[~filtered_catalog_df['sku'].isin(matched_base_skus)]
        logger.info(f"Recall candidates after subtracting matched SKUs: {len(recall_candidates_df)}")

        if recall_candidates_df.empty:
            raise ValueError("No recall candidates available after filtering and subtraction!")

        # Sampling
        sampler = Sampler(tenant_config["sampling_strategy"])
        sampled_df = sampler.sample(recall_candidates_df)

        logger.info(f"Total samples generated: {len(sampled_df)}")

        if sampled_df.empty:
            raise ValueError("Sampling resulted in 0 samples! Check your strategy or available candidates.")


        # Initialize DataWriter
        data_writer = DataWriter(output_config=tenant_config["output"], customer_name=customer_name)

        # Load audit log
        audit_log_df = data_writer.load_audit_log()

        # Exclude already sampled SKUs
        recall_candidates_df = data_writer.exclude_already_sampled(recall_candidates_df, audit_log_df)
        logger.info(f"Recall candidates after excluding already-sampled SKUs: {len(recall_candidates_df)}")

        if recall_candidates_df.empty:
            raise ValueError("No recall candidates left after excluding previously sampled SKUs!")

        if dry_run:
            logger.info("Dry-run mode active. Exiting after data loading and filtering.")
            logger.info(f"Dry-run summary: {len(recall_candidates_df)} candidates available for sampling.")
            return

        
        # Metadata Preparation
        metadata = {
            "customer_name": customer_name,
            "run_date": datetime.now().strftime("%Y-%m-%d"),
            "sampling_strategy": tenant_config["sampling_strategy"],
            "total_candidates_before_sampling": len(recall_candidates_df),
            "total_samples_after_sampling": len(sampled_df),
            "filters_applied": tenant_config.get("filters", {}),
            "config_hash": calculate_config_hash(tenant_config)
        }

        # Write output
        data_writer.write_samples(sampled_df, metadata)

        # Update audit log
        data_writer.update_audit_log(sampled_df['base_sku'].tolist(), metadata['sampling_strategy']['type'])

        logger.info("Recall sampling run completed successfully.")
        alert_handler.send_alert(
            subject=f"✅ Recall Sampler Success - {customer_name}",
            message_body=f"Recall sampling completed successfully for {customer_name} with {len(sampled_df)} samples."
        )

    except Exception as e:
        logger.error(f"Recall Audit Sampler FAILED. Error: {str(e)}", exc_info=True)
        try:
            alert_handler.send_alert(
                subject=f"❌ Recall Sampler FAILED - {tenant_config.get('customer_name', 'Unknown')}",
                message_body=f"Recall sampler failed. Reason: {str(e)}"
            )
        except Exception as alert_error:
            logger.error(f"Failed to send alert: {str(alert_error)}", exc_info=True)
        raise

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python run_recall_sampler.py <path_to_config.json> [--dry-run]")
        sys.exit(1)

    config_path = sys.argv[1]
    dry_run = False
    if len(sys.argv) > 2 and sys.argv[2] == "--dry-run":
        dry_run = True

    main(config_path, dry_run=dry_run)
