#!/usr/bin/env python
"""
Aggregate metrics from the latest performance_metrics.txt file found via a pattern
and perform final validation checks based on Sharpe Ratio and Max Drawdown.

Ref: revisions.txt Section 8
"""

import argparse
import glob
import os
import re
import sys
import logging

# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

METRIC_SHARPE = "Annualized Sharpe Ratio (Re-centred)"
METRIC_MAX_DD = "Max Drawdown (%)"

def parse_metric_value(line: str, metric_name: str) -> float | None:
    """Extracts float value from a 'metric_name: value' line."""
    # Match lines like "Metric Name: 12.3456" or "Metric Name (%): 12.3456"
    # Handles potential variations in spacing and optional % sign near name
    pattern = rf"^\s*{re.escape(metric_name)}\s*\(?%?\)?\s*:\s*(-?\d*\.?\d+)"
    match = re.search(pattern, line, re.IGNORECASE)
    if match:
        try:
            return float(match.group(1))
        except ValueError:
            logger.warning(f"Could not convert value '{match.group(1)}' to float for metric '{metric_name}'.")
    return None

def main():
    parser = argparse.ArgumentParser(
        description="Parse latest metrics file and validate Sharpe/Max Drawdown."
    )
    parser.add_argument(
        'metrics_pattern',
        type=str,
        help='Glob pattern for performance_metrics.txt files (e.g., "results/*/performance_metrics*.txt")'
    )
    parser.add_argument(
        '--min_sharpe', type=float, default=1.2,
        help='Minimum acceptable Annualized Sharpe Ratio (Re-centred).'
    )
    parser.add_argument(
        '--max_drawdown_pct', type=float, default=15.0,
        help='Maximum acceptable Max Drawdown Percentage.'
    )
    args = parser.parse_args()

    logger.info(f"Searching for metrics files using pattern: {args.metrics_pattern}")
    try:
        metrics_files = sorted(glob.glob(args.metrics_pattern))
    except Exception as e:
        logger.error(f"Error during glob pattern expansion '{args.metrics_pattern}': {e}")
        sys.exit(1)

    if not metrics_files:
        logger.error(f"No metrics files found matching pattern: {args.metrics_pattern}")
        sys.exit(1)

    latest_file = metrics_files[-1]
    logger.info(f"Processing latest metrics file: {latest_file}")

    sharpe_value = None
    max_dd_value = None

    try:
        with open(latest_file, 'r') as f:
            for line in f:
                # Use the robust parsing function
                if sharpe_value is None:
                    parsed_sharpe = parse_metric_value(line, METRIC_SHARPE)
                    if parsed_sharpe is not None:
                        sharpe_value = parsed_sharpe
                
                if max_dd_value is None:
                    parsed_dd = parse_metric_value(line, METRIC_MAX_DD)
                    if parsed_dd is not None:
                        max_dd_value = parsed_dd

                # Stop reading if both metrics found
                if sharpe_value is not None and max_dd_value is not None:
                    break
                    
    except FileNotFoundError:
        logger.error(f"Could not find file: {latest_file}")
        sys.exit(1)
    except Exception as e:
        logger.error(f"Error reading or parsing file {latest_file}: {e}")
        sys.exit(1)

    # --- Perform Checks --- #
    checks_passed = True
    fail_reasons = []

    logger.info(f"Extracted Metrics: Sharpe={sharpe_value}, Max Drawdown={max_dd_value}%")

    # Check Sharpe Ratio
    if sharpe_value is None:
        logger.error(f"'{METRIC_SHARPE}' not found or could not be parsed.")
        fail_reasons.append("Sharpe ratio missing/unparseable")
        checks_passed = False
    elif sharpe_value < args.min_sharpe:
        logger.error(f"VALIDATION FAIL: Sharpe Ratio ({sharpe_value:.3f}) is below threshold ({args.min_sharpe:.3f})")
        fail_reasons.append(f"Sharpe ({sharpe_value:.3f}) < {args.min_sharpe:.3f}")
        checks_passed = False
    else:
        logger.info(f"VALIDATION PASS: Sharpe Ratio ({sharpe_value:.3f}) >= {args.min_sharpe:.3f}")

    # Check Max Drawdown
    if max_dd_value is None:
        logger.error(f"'{METRIC_MAX_DD}' not found or could not be parsed.")
        fail_reasons.append("Max Drawdown missing/unparseable")
        checks_passed = False
    elif max_dd_value > args.max_drawdown_pct:
        logger.error(f"VALIDATION FAIL: Max Drawdown ({max_dd_value:.2f}%) exceeds threshold ({args.max_drawdown_pct:.2f}%)")
        fail_reasons.append(f"Max Drawdown ({max_dd_value:.2f}%) > {args.max_drawdown_pct:.2f}%")
        checks_passed = False
    else:
        logger.info(f"VALIDATION PASS: Max Drawdown ({max_dd_value:.2f}%) <= {args.max_drawdown_pct:.2f}%")

    # --- Exit Status --- #
    if checks_passed:
        logger.info("All final metric checks passed.")
        sys.exit(0)
    else:
        logger.error(f"One or more final metric checks failed: {', '.join(fail_reasons)}")
        sys.exit(1)

if __name__ == "__main__":
    main() 