#!/usr/bin/env python3
"""
Google Ads Agency Agent - Hourly Monitor
Run every hour to catch urgent issues in real-time.
"""

import sys
import argparse
from datetime import datetime, timedelta
from typing import List, Optional, Dict
import json

from core import (
    get_client, get_thresholds, ACCOUNTS, run_query,
    micros_to_currency, format_currency,
    Issue, Severity, DATA_DIR,
)


def load_hourly_state() -> Dict:
    """Load previous hourly check state for comparison."""
    state_file = DATA_DIR / "hourly_state.json"
    if state_file.exists():
        with open(state_file) as f:
            return json.load(f)
    return {}


def save_hourly_state(state: Dict):
    """Save current hourly state."""
    state_file = DATA_DIR / "hourly_state.json"
    with open(state_file, 'w') as f:
        json.dump(state, f, indent=2)


def check_budget_pacing_hourly(client, account_key: str) -> List[Issue]:
    """
    Check real-time budget pacing.
    Alert if spending too fast or too slow for time of day.
    """
    account = ACCOUNTS[account_key]
    thresholds = get_thresholds()
    issues = []
    
    # Get today's spend
    query = """
        SELECT
            campaign.name,
            campaign.id,
            campaign_budget.amount_micros,
            metrics.cost_micros
        FROM campaign
        WHERE segments.date DURING TODAY
          AND campaign.status = 'ENABLED'
          AND metrics.cost_micros > 0
    """
    
    results = run_query(client, account['id'], query)
    
    # Calculate expected spend based on time of day
    now = datetime.now()
    hours_passed = now.hour + (now.minute / 60)
    expected_pacing = hours_passed / 24  # 0 to 1 representing day progress
    
    for row in results:
        if not row.campaign_budget.amount_micros:
            continue
        
        daily_budget = micros_to_currency(row.campaign_budget.amount_micros)
        today_spend = micros_to_currency(row.metrics.cost_micros)
        
        if daily_budget > 0:
            actual_pacing = today_spend / daily_budget
            
            # Alert if significantly over-pacing (will exhaust budget early)
            if actual_pacing > expected_pacing * 1.5 and today_spend > daily_budget * 0.7:
                issues.append(Issue(
                    type="BUDGET_EXHAUSTING",
                    severity=Severity.HIGH,
                    account=account['name'],
                    message=f"'{row.campaign.name}' burning budget fast: {format_currency(today_spend)} spent ({actual_pacing:.0%} of daily) with {24 - hours_passed:.1f}h left",
                    details={
                        "campaign": row.campaign.name,
                        "campaign_id": str(row.campaign.id),
                        "daily_budget": daily_budget,
                        "today_spend": today_spend,
                        "pacing_vs_expected": actual_pacing / expected_pacing if expected_pacing > 0 else 0,
                    }
                ))
            
            # Alert if significantly under-pacing after midday
            elif hours_passed > 12 and actual_pacing < expected_pacing * 0.4 and daily_budget > 50:
                issues.append(Issue(
                    type="LOW_DELIVERY",
                    severity=Severity.MEDIUM,
                    account=account['name'],
                    message=f"'{row.campaign.name}' underdelivering: only {format_currency(today_spend)} spent ({actual_pacing:.0%} of daily budget)",
                    details={
                        "campaign": row.campaign.name,
                        "campaign_id": str(row.campaign.id),
                        "daily_budget": daily_budget,
                        "today_spend": today_spend,
                    }
                ))
    
    return issues


def check_cpc_spike(client, account_key: str) -> List[Issue]:
    """
    Check for sudden CPC spikes compared to yesterday.
    """
    account = ACCOUNTS[account_key]
    thresholds = get_thresholds()
    issues = []
    
    # Today's metrics
    query_today = """
        SELECT
            campaign.name,
            metrics.cost_micros,
            metrics.clicks
        FROM campaign
        WHERE segments.date DURING TODAY
          AND campaign.status = 'ENABLED'
          AND metrics.clicks > 0
    """
    
    # Yesterday's metrics
    query_yesterday = """
        SELECT
            campaign.name,
            metrics.cost_micros,
            metrics.clicks
        FROM campaign
        WHERE segments.date DURING YESTERDAY
          AND campaign.status = 'ENABLED'
          AND metrics.clicks > 0
    """
    
    today_results = run_query(client, account['id'], query_today)
    yesterday_results = run_query(client, account['id'], query_yesterday)
    
    # Build yesterday lookup
    yesterday_cpc = {}
    for row in yesterday_results:
        cost = micros_to_currency(row.metrics.cost_micros)
        clicks = row.metrics.clicks
        if clicks > 10:  # Need meaningful data
            yesterday_cpc[row.campaign.name] = cost / clicks
    
    # Compare today
    for row in today_results:
        cost = micros_to_currency(row.metrics.cost_micros)
        clicks = row.metrics.clicks
        
        if clicks < 5:  # Not enough data yet
            continue
        
        today_cpc = cost / clicks
        yesterday_campaign_cpc = yesterday_cpc.get(row.campaign.name)
        
        if yesterday_campaign_cpc and yesterday_campaign_cpc > 0:
            spike = (today_cpc - yesterday_campaign_cpc) / yesterday_campaign_cpc
            
            if spike > thresholds['cpc_spike_threshold']:
                issues.append(Issue(
                    type="CPC_SPIKE",
                    severity=Severity.MEDIUM,
                    account=account['name'],
                    message=f"'{row.campaign.name}' CPC spiked {spike:.0%}: {format_currency(yesterday_campaign_cpc)} → {format_currency(today_cpc)}",
                    details={
                        "campaign": row.campaign.name,
                        "today_cpc": today_cpc,
                        "yesterday_cpc": yesterday_campaign_cpc,
                        "spike_percent": spike * 100,
                    }
                ))
    
    return issues


def check_conversion_gap(client, account_key: str) -> List[Issue]:
    """
    Check if there's an unusual gap in conversions during business hours.
    """
    account = ACCOUNTS[account_key]
    thresholds = get_thresholds()
    issues = []
    
    now = datetime.now()
    
    # Only check during business hours (9am - 9pm local)
    if now.hour < 9 or now.hour > 21:
        return issues
    
    # Get today's conversions
    query = """
        SELECT
            metrics.conversions,
            metrics.cost_micros
        FROM customer
        WHERE segments.date DURING TODAY
    """
    
    results = run_query(client, account['id'], query)
    
    if not results:
        return issues
    
    row = results[0]
    today_conv = row.metrics.conversions
    today_cost = micros_to_currency(row.metrics.cost_micros)
    
    # Load state to check last conversion time
    state = load_hourly_state()
    account_state = state.get(account_key, {})
    
    last_conv_check = account_state.get('last_conversion_count', 0)
    last_conv_time = account_state.get('last_conversion_time')
    
    # Update state
    if today_conv > last_conv_check:
        account_state['last_conversion_count'] = today_conv
        account_state['last_conversion_time'] = now.isoformat()
    
    state[account_key] = account_state
    save_hourly_state(state)
    
    # If we had conversions earlier but none in last N hours while spending
    if last_conv_time and today_cost > 100:
        last_conv_dt = datetime.fromisoformat(last_conv_time)
        hours_since = (now - last_conv_dt).total_seconds() / 3600
        
        if hours_since > thresholds['conversion_gap_hours']:
            issues.append(Issue(
                type="CONVERSION_GAP",
                severity=Severity.HIGH,
                account=account['name'],
                message=f"No conversions in {hours_since:.1f}h while spending {format_currency(today_cost)} today",
                details={
                    "hours_since_conversion": hours_since,
                    "today_spend": today_cost,
                    "today_conversions": today_conv,
                }
            ))
    
    return issues


def check_account_errors(client, account_key: str) -> List[Issue]:
    """
    Check for account-level issues like payment problems or policy violations.
    """
    account = ACCOUNTS[account_key]
    issues = []
    
    # Check account status
    query = """
        SELECT
            customer.descriptive_name,
            customer.status
        FROM customer
        LIMIT 1
    """
    
    try:
        results = run_query(client, account['id'], query)
        
        if results:
            status = results[0].customer.status.name
            if status != 'ENABLED':
                issues.append(Issue(
                    type="ACCOUNT_STATUS",
                    severity=Severity.CRITICAL,
                    account=account['name'],
                    message=f"Account status is {status}! Ads may not be running.",
                    details={"status": status}
                ))
    except Exception as e:
        # API errors might indicate account issues
        if 'NOT_ALLOWED' in str(e) or 'PERMISSION_DENIED' in str(e):
            issues.append(Issue(
                type="ACCOUNT_ACCESS",
                severity=Severity.CRITICAL,
                account=account['name'],
                message=f"Cannot access account - check permissions or account status",
                details={"error": str(e)}
            ))
    
    return issues


def run_hourly_monitor() -> List[Issue]:
    """Run all hourly monitoring checks."""
    client = get_client()
    all_issues = []
    
    checks = [
        ("Budget Pacing", check_budget_pacing_hourly),
        ("CPC Spike", check_cpc_spike),
        ("Conversion Gap", check_conversion_gap),
        ("Account Status", check_account_errors),
    ]
    
    for account_key in ACCOUNTS:
        account = ACCOUNTS[account_key]
        print(f"Monitoring {account['name']}...", file=sys.stderr)
        
        for check_name, check_fn in checks:
            try:
                issues = check_fn(client, account_key)
                all_issues.extend(issues)
            except Exception as e:
                print(f"  {check_name} error: {e}", file=sys.stderr)
    
    return all_issues


def format_alert_message(issues: List[Issue]) -> Optional[str]:
    """Format issues for immediate alert. Returns None if no issues."""
    if not issues:
        return None
    
    # Filter to only high-priority issues worth alerting
    urgent = [i for i in issues if i.severity in [Severity.CRITICAL, Severity.HIGH]]
    
    if not urgent:
        return None
    
    message = "🚨 **Google Ads Alert**\n\n"
    
    for issue in urgent:
        emoji = "🔴" if issue.severity == Severity.CRITICAL else "⚠️"
        message += f"{emoji} **{issue.type}** ({issue.account})\n"
        message += f"   {issue.message}\n\n"
    
    return message


def main():
    parser = argparse.ArgumentParser(description="Hourly Google Ads monitoring")
    parser.add_argument("--json", action="store_true", help="Output as JSON")
    parser.add_argument("--quiet", action="store_true", help="Only output if urgent issues")
    args = parser.parse_args()
    
    issues = run_hourly_monitor()
    
    if args.json:
        output = {
            "issues": [i.to_dict() for i in issues],
            "timestamp": datetime.now().isoformat(),
        }
        print(json.dumps(output, indent=2))
    elif args.quiet:
        alert = format_alert_message(issues)
        if alert:
            print(alert)
    else:
        if issues:
            for issue in issues:
                print(issue.format_telegram())
                print()
        else:
            print("✅ All accounts look healthy")


if __name__ == "__main__":
    main()
