#!/usr/bin/env python3
"""
Google Ads Agency Agent - Action Executor
Execute approved recommendations with safety checks.
"""

import sys
import argparse
from datetime import datetime
import uuid

from typing import Tuple, List

from core import (
    get_client, ACCOUNTS, 
    load_recommendations, save_recommendations,
    ActionType, Recommendation, ChangeLog, log_change,
    format_currency,
)


def find_account_id_by_name(name: str) -> str:
    """Find account ID from name."""
    for key, account in ACCOUNTS.items():
        if account['name'] == name:
            return account['id']
    return None


def pause_keyword(client, customer_id: str, ad_group_id: str, keyword_id: str) -> bool:
    """Pause a keyword."""
    ad_group_criterion_service = client.get_service("AdGroupCriterionService")
    
    ad_group_criterion_operation = client.get_type("AdGroupCriterionOperation")
    ad_group_criterion = ad_group_criterion_operation.update
    
    ad_group_criterion.resource_name = ad_group_criterion_service.ad_group_criterion_path(
        customer_id, ad_group_id, keyword_id
    )
    ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.PAUSED
    
    # Set update mask
    client.copy_from(
        ad_group_criterion_operation.update_mask,
        client.get_type("FieldMask")(paths=["status"])
    )
    
    try:
        response = ad_group_criterion_service.mutate_ad_group_criteria(
            customer_id=customer_id,
            operations=[ad_group_criterion_operation]
        )
        return True
    except Exception as e:
        print(f"Error pausing keyword: {e}", file=sys.stderr)
        return False


def enable_keyword(client, customer_id: str, ad_group_id: str, keyword_id: str) -> bool:
    """Enable a paused keyword."""
    ad_group_criterion_service = client.get_service("AdGroupCriterionService")
    
    ad_group_criterion_operation = client.get_type("AdGroupCriterionOperation")
    ad_group_criterion = ad_group_criterion_operation.update
    
    ad_group_criterion.resource_name = ad_group_criterion_service.ad_group_criterion_path(
        customer_id, ad_group_id, keyword_id
    )
    ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED
    
    client.copy_from(
        ad_group_criterion_operation.update_mask,
        client.get_type("FieldMask")(paths=["status"])
    )
    
    try:
        response = ad_group_criterion_service.mutate_ad_group_criteria(
            customer_id=customer_id,
            operations=[ad_group_criterion_operation]
        )
        return True
    except Exception as e:
        print(f"Error enabling keyword: {e}", file=sys.stderr)
        return False


def add_negative_keyword(client, customer_id: str, campaign_id: str, keyword_text: str, match_type: str = "EXACT") -> bool:
    """Add a negative keyword to a campaign."""
    campaign_criterion_service = client.get_service("CampaignCriterionService")
    
    campaign_criterion_operation = client.get_type("CampaignCriterionOperation")
    campaign_criterion = campaign_criterion_operation.create
    
    campaign_criterion.campaign = client.get_service("CampaignService").campaign_path(
        customer_id, campaign_id
    )
    campaign_criterion.negative = True
    campaign_criterion.keyword.text = keyword_text
    campaign_criterion.keyword.match_type = getattr(
        client.enums.KeywordMatchTypeEnum, match_type
    )
    
    try:
        response = campaign_criterion_service.mutate_campaign_criteria(
            customer_id=customer_id,
            operations=[campaign_criterion_operation]
        )
        return True
    except Exception as e:
        print(f"Error adding negative keyword: {e}", file=sys.stderr)
        return False


def adjust_campaign_budget(client, customer_id: str, campaign_id: str, new_budget_micros: int) -> bool:
    """Adjust campaign budget."""
    campaign_budget_service = client.get_service("CampaignBudgetService")
    ga_service = client.get_service("GoogleAdsService")
    
    # First, get the current budget resource name
    query = f"""
        SELECT campaign.campaign_budget
        FROM campaign
        WHERE campaign.id = {campaign_id}
    """
    
    try:
        response = ga_service.search(customer_id=customer_id, query=query)
        budget_resource_name = None
        for row in response:
            budget_resource_name = row.campaign.campaign_budget
            break
        
        if not budget_resource_name:
            print("Could not find campaign budget", file=sys.stderr)
            return False
        
        # Update the budget
        budget_operation = client.get_type("CampaignBudgetOperation")
        budget = budget_operation.update
        budget.resource_name = budget_resource_name
        budget.amount_micros = new_budget_micros
        
        client.copy_from(
            budget_operation.update_mask,
            client.get_type("FieldMask")(paths=["amount_micros"])
        )
        
        response = campaign_budget_service.mutate_campaign_budgets(
            customer_id=customer_id,
            operations=[budget_operation]
        )
        return True
    except Exception as e:
        print(f"Error adjusting budget: {e}", file=sys.stderr)
        return False


def execute_recommendation(rec: Recommendation) -> Tuple[bool, str]:
    """Execute a single recommendation."""
    client = get_client()
    customer_id = find_account_id_by_name(rec.account)
    
    if not customer_id:
        return False, f"Could not find account: {rec.account}"
    
    success = False
    message = ""
    
    if rec.action == ActionType.PAUSE_KEYWORD:
        ad_group_id = rec.details.get('ad_group_id')
        keyword_id = rec.details.get('keyword_id')
        
        if not ad_group_id or not keyword_id:
            return False, "Missing ad_group_id or keyword_id in recommendation"
        
        success = pause_keyword(client, customer_id, ad_group_id, keyword_id)
        message = f"Paused keyword '{rec.details.get('keyword_text', 'unknown')}'"
        
    elif rec.action == ActionType.ENABLE_KEYWORD:
        ad_group_id = rec.details.get('ad_group_id')
        keyword_id = rec.details.get('keyword_id')
        
        if not ad_group_id or not keyword_id:
            return False, "Missing ad_group_id or keyword_id in recommendation"
        
        success = enable_keyword(client, customer_id, ad_group_id, keyword_id)
        message = f"Enabled keyword '{rec.details.get('keyword_text', 'unknown')}'"
        
    elif rec.action == ActionType.ADD_NEGATIVE:
        campaign_id = rec.details.get('campaign_id')
        search_term = rec.details.get('search_term')
        match_type = rec.details.get('negative_type', 'EXACT')
        
        if not campaign_id or not search_term:
            return False, "Missing campaign_id or search_term"
        
        success = add_negative_keyword(client, customer_id, campaign_id, search_term, match_type)
        message = f"Added negative keyword '{search_term}' [{match_type}]"
        
    elif rec.action == ActionType.ADJUST_BUDGET:
        campaign_id = rec.details.get('campaign_id')
        new_budget = rec.details.get('new_budget_micros')
        
        if not campaign_id or not new_budget:
            return False, "Missing campaign_id or new_budget_micros"
        
        success = adjust_campaign_budget(client, customer_id, campaign_id, new_budget)
        message = f"Adjusted budget to {format_currency(new_budget / 1_000_000)}"
        
    else:
        return False, f"Unsupported action type: {rec.action}"
    
    # Log the change
    if success:
        change = ChangeLog(
            id=f"change-{uuid.uuid4().hex[:8]}",
            action=rec.action,
            account=rec.account,
            target=rec.target,
            target_id=rec.target_id,
            old_value="active" if rec.action == ActionType.PAUSE_KEYWORD else None,
            new_value="paused" if rec.action == ActionType.PAUSE_KEYWORD else None,
            reason=rec.reason,
            executed_at=datetime.now(),
            recommendation_id=rec.id,
        )
        log_change(change)
    
    return success, message


def list_pending_recommendations() -> List[Recommendation]:
    """Get all pending recommendations."""
    recs = load_recommendations()
    return [r for r in recs if r.status == "pending"]


def approve_recommendation(rec_id: str) -> Tuple[bool, str]:
    """Approve and execute a recommendation."""
    recs = load_recommendations()
    
    for rec in recs:
        if rec.id == rec_id:
            if rec.status != "pending":
                return False, f"Recommendation is already {rec.status}"
            
            # Execute
            success, message = execute_recommendation(rec)
            
            if success:
                rec.status = "executed"
                save_recommendations(recs)
                return True, f"✅ Executed: {message}"
            else:
                return False, f"❌ Failed: {message}"
    
    return False, f"Recommendation {rec_id} not found"


def reject_recommendation(rec_id: str, reason: str = None) -> bool:
    """Reject a recommendation."""
    recs = load_recommendations()
    
    for rec in recs:
        if rec.id == rec_id:
            rec.status = "rejected"
            save_recommendations(recs)
            return True
    
    return False


def format_pending_recommendations() -> str:
    """Format pending recommendations for display."""
    pending = list_pending_recommendations()
    
    if not pending:
        return "✅ No pending recommendations."
    
    output = f"📋 **{len(pending)} Pending Recommendations**\n\n"
    
    for i, rec in enumerate(pending[:10], 1):
        conf_emoji = "🟢" if rec.confidence > 0.8 else "🟡" if rec.confidence > 0.6 else "🟠"
        
        output += f"**{i}. {rec.action.value.replace('_', ' ').title()}** {conf_emoji}\n"
        output += f"   Target: {rec.target}\n"
        output += f"   Reason: {rec.reason}\n"
        output += f"   Impact: {rec.expected_impact}\n"
        output += f"   ID: `{rec.id}`\n\n"
    
    if len(pending) > 10:
        output += f"_...and {len(pending) - 10} more_\n"
    
    output += "\n**Commands:**\n"
    output += "• `approve [id]` - Execute recommendation\n"
    output += "• `reject [id]` - Dismiss recommendation\n"
    output += "• `approve all` - Execute all (be careful!)\n"
    
    return output


def main():
    parser = argparse.ArgumentParser(description="Execute Google Ads recommendations")
    parser.add_argument("action", choices=["list", "approve", "reject", "approve-all"])
    parser.add_argument("--id", help="Recommendation ID")
    parser.add_argument("--reason", help="Rejection reason")
    parser.add_argument("--dry-run", action="store_true", help="Don't actually execute")
    args = parser.parse_args()
    
    if args.action == "list":
        print(format_pending_recommendations())
        
    elif args.action == "approve":
        if not args.id:
            print("Error: --id required for approve", file=sys.stderr)
            sys.exit(1)
        
        if args.dry_run:
            print(f"[DRY RUN] Would approve: {args.id}")
        else:
            success, message = approve_recommendation(args.id)
            print(message)
            sys.exit(0 if success else 1)
            
    elif args.action == "reject":
        if not args.id:
            print("Error: --id required for reject", file=sys.stderr)
            sys.exit(1)
        
        success = reject_recommendation(args.id, args.reason)
        if success:
            print(f"✅ Rejected recommendation {args.id}")
        else:
            print(f"❌ Could not find recommendation {args.id}")
            sys.exit(1)
            
    elif args.action == "approve-all":
        pending = list_pending_recommendations()
        
        if not pending:
            print("No pending recommendations.")
            return
        
        print(f"⚠️ About to execute {len(pending)} recommendations...")
        
        if args.dry_run:
            for rec in pending:
                print(f"  [DRY RUN] Would execute: {rec.action.value} on {rec.target}")
        else:
            successes = 0
            failures = 0
            
            for rec in pending:
                success, message = approve_recommendation(rec.id)
                if success:
                    print(f"  ✅ {message}")
                    successes += 1
                else:
                    print(f"  ❌ {message}")
                    failures += 1
            
            print(f"\n**Results:** {successes} succeeded, {failures} failed")


if __name__ == "__main__":
    main()
