#!/usr/bin/env python3
"""
Campaign Batch Sender
Sends templated campaign messages to queued customers with throttling and time windows.
"""
import argparse
import sys
import os
import random
import time as time_module
from datetime import datetime, time
from zoneinfo import ZoneInfo
from typing import List, Dict, Any, Optional
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from utils.tools import get_client, log_message, sanitize_internal_codes


def check_opt_out_status(agreement_number: str, channel: str = 'whatsapp') -> bool:
    """
    Check if customer has opted out within last 2 years.
    
    Args:
        agreement_number: Agreement number
        channel: Communication channel (default: whatsapp)
    
    Returns:
        True if opted out (suppress sending), False otherwise
    """
    try:
        client = get_client()
        
        # Calculate 2 years ago
        tz = ZoneInfo("Europe/London")
        now_dt = datetime.now(tz)
        from datetime import timedelta
        two_years_ago = now_dt - timedelta(days=730)
        
        # Query opt_out table
        result = client.table('opt_out') \
            .select('id, opted_out_at') \
            .eq('agreement_number', agreement_number) \
            .eq('channel', channel) \
            .gte('opted_out_at', two_years_ago.isoformat()) \
            .execute()
        
        return len(result.data) > 0 if result.data else False
    
    except Exception as e:
        # If table doesn't exist or other error, allow sending
        return False


def check_send_window(now: Optional[datetime] = None) -> bool:
    """
    Check if current time is within campaign send window (10:15–11:45 UK time).
    
    Args:
        now: Optional datetime to check (defaults to current UK time)
    
    Returns:
        True if within send window, False otherwise
    """
    tz = ZoneInfo("Europe/London")
    now = now or datetime.now(tz)
    
    # Send window: 10:15–11:45
    return time(10, 15) <= now.time() <= time(11, 45)


def get_campaign_batch(tag: str) -> Dict[str, Any]:
    """
    Look up campaign_batch by tag.
    
    Args:
        tag: Campaign batch tag
    
    Returns:
        Dict with id and summary
    
    Raises:
        SystemExit if batch not found
    """
    client = get_client()
    
    result = client.table('campaign_batch') \
        .select('id, tag, summary') \
        .eq('tag', tag) \
        .execute()
    
    if not result.data or len(result.data) == 0:
        print(f"❌ Campaign batch not found: {tag}")
        sys.exit(1)
    
    return result.data[0]


def get_queued_items(tag: str, limit: int) -> List[Dict[str, Any]]:
    """
    Fetch queued campaign pool items for this tag.
    
    Args:
        tag: Campaign tag
        limit: Maximum number of items to fetch
    
    Returns:
        List of campaign_pool items with status='queued'
    """
    client = get_client()
    
    result = client.table('campaign_pool') \
        .select('id, agreement_id') \
        .eq('campaign_tag', tag) \
        .eq('status', 'queued') \
        .order('id', desc=False) \
        .limit(limit) \
        .execute()
    
    return result.data


def get_customer_name(agreement_id: int) -> str:
    """
    Get customer first_name for an agreement.
    
    Args:
        agreement_id: Agreement ID
    
    Returns:
        Customer first name or 'there'
    """
    client = get_client()
    
    result = client.table('agreement') \
        .select('customer!agreement_customer_id_fkey(first_name)') \
        .eq('id', agreement_id) \
        .execute()
    
    if result.data and len(result.data) > 0:
        customer = result.data[0].get('customer') or {}
        return customer.get('first_name', 'there')
    
    return 'there'


def get_agreement_number(agreement_id: int) -> str:
    """
    Get agreement_number for an agreement.
    
    Args:
        agreement_id: Agreement ID
    
    Returns:
        Agreement number or 'unknown'
    """
    client = get_client()
    
    result = client.table('agreement') \
        .select('agreement_number') \
        .eq('id', agreement_id) \
        .execute()
    
    if result.data and len(result.data) > 0:
        return result.data[0].get('agreement_number', 'unknown')
    
    return 'unknown'


def get_pressure_flag(agreement_id: int) -> Optional[str]:
    """
    Get pressure_flag from v_over_contact_flag for an agreement.
    
    Args:
        agreement_id: Agreement ID
    
    Returns:
        Pressure flag ('red', 'amber', 'green') or None
    """
    client = get_client()
    
    result = client.table('v_over_contact_flag') \
        .select('pressure_flag') \
        .eq('agreement_id', agreement_id) \
        .execute()
    
    if result.data and len(result.data) > 0:
        return result.data[0].get('pressure_flag')
    
    return None


def check_campaign_override(agreement_id: int, batch_id: int) -> bool:
    """
    Check if a campaign override exists for this agreement and batch.
    
    Args:
        agreement_id: Agreement ID
        batch_id: Campaign batch ID
    
    Returns:
        True if override exists, False otherwise
    """
    client = get_client()
    
    result = client.table('campaign_override') \
        .select('id') \
        .eq('agreement_id', agreement_id) \
        .eq('batch_id', batch_id) \
        .execute()
    
    return len(result.data) > 0 if result.data else False


def get_template(slug: str) -> Optional[str]:
    """
    Get template body by slug.
    
    Args:
        slug: Template slug
    
    Returns:
        Template body or None
    """
    client = get_client()
    
    result = client.table('reply_template') \
        .select('body') \
        .eq('slug', slug) \
        .eq('active', True) \
        .execute()
    
    if result.data and len(result.data) > 0:
        return result.data[0]['body']
    
    return None


def render_template(template_body: str, first_name: str, campaign_tag: str = None) -> str:
    """
    Render template with placeholders.
    Sanitizes internal campaign codes to prevent mail-merge feel.
    
    Args:
        template_body: Template string
        first_name: Customer first name
        campaign_tag: Campaign tag (NOT used in message - kept for compatibility only)
    
    Returns:
        Rendered message text with internal codes removed
    """
    text = template_body
    text = text.replace('{{first_name}}', first_name)
    
    # DO NOT include campaign_tag in message - removes "mail-merge" feel
    # If template has {{campaign_tag}}, strip it out
    text = text.replace('{{campaign_tag}}', '')
    
    # Sanitize any internal codes that may have leaked into template
    text = sanitize_internal_codes(text)
    
    return text.strip()


def mark_pool_item_done(pool_id: int) -> None:
    """
    Update campaign_pool status to 'done'.
    
    Args:
        pool_id: Campaign pool item ID
    """
    client = get_client()
    
    client.table('campaign_pool') \
        .update({'status': 'done'}) \
        .eq('id', pool_id) \
        .execute()


def run(tag: str, channel: str, limit: int, throttle_min: int, throttle_max: int, ignore_window: bool = False) -> None:
    """
    Main send logic.
    
    Args:
        tag: Campaign tag
        channel: Communication channel
        limit: Maximum messages to send
        throttle_min: Minimum throttle seconds
        throttle_max: Maximum throttle seconds
        ignore_window: Skip UK time window check and startup jitter (default: False)
    """
    print("=" * 80)
    print("📨 SEND CAMPAIGN BATCH")
    print("=" * 80)
    print(f"Tag: {tag}")
    print(f"Channel: {channel}")
    print(f"Limit: {limit}")
    print(f"Throttle: {throttle_min}-{throttle_max} seconds")
    if ignore_window:
        print("⚠️  Window check DISABLED (--ignore-window)")
    print()
    
    # Check send window (unless ignore_window is set)
    if not ignore_window:
        tz = ZoneInfo("Europe/London")
        now = datetime.now(tz)
        
        if not check_send_window(now):
            print(f"⏰ Outside send window (current time: {now.strftime('%H:%M:%S')} UK)")
            print("   Send window: 10:15–11:45 UK time")
            print("   Exiting without sending (exit 0)")
            print("=" * 80)
            return
        
        print(f"✅ Within send window (current time: {now.strftime('%H:%M:%S')} UK)")
        
        # Startup jitter (0-20 minutes)
        jitter_minutes = random.randint(0, 20)
        jitter_seconds = jitter_minutes * 60
        
        if jitter_minutes > 0:
            print(f"\n⏳ Startup jitter: sleeping {jitter_minutes} minute(s)...")
            time_module.sleep(jitter_seconds)
            print(f"   ✅ Jitter complete")
        else:
            print(f"\n⏳ Startup jitter: 0 minutes (proceeding immediately)")
    else:
        print("⚠️  Skipping window check and startup jitter (--ignore-window enabled)")
    
    # Get campaign batch
    print(f"\n📋 Looking up campaign batch...")
    batch = get_campaign_batch(tag)
    batch_id = batch['id']
    summary = batch.get('summary', 'N/A')
    
    print(f"   ✅ Found batch (ID: {batch_id})")
    print(f"   Summary: {summary}")
    
    # Get template
    print(f"\n📝 Loading offer_intro template...")
    template_body = get_template('offer_intro')
    
    if not template_body:
        print("   ❌ Template 'offer_intro' not found or inactive")
        sys.exit(1)
    
    print(f"   ✅ Template loaded ({len(template_body)} chars)")
    
    # Get queued items
    print(f"\n🔍 Fetching queued items (limit={limit})...")
    items = get_queued_items(tag, limit)
    total = len(items)
    
    print(f"   Found {total} queued item(s)")
    
    if total == 0:
        print("\n   ℹ️  No queued items to send")
        print("=" * 80)
        return
    
    # Send messages
    print(f"\n📤 Sending messages (with opt-out and pressure checks)...")
    sent_count = 0
    skipped_red = 0
    skipped_opt_out = 0
    warned_amber = 0
    
    for idx, item in enumerate(items, 1):
        pool_id = item['id']
        agreement_id = item['agreement_id']
        
        print(f"\n   [{idx}/{total}] Agreement ID: {agreement_id}")
        
        # Get agreement_number for opt-out check
        agreement_number = get_agreement_number(agreement_id)
        
        # Check opt-out status (suppress if opted out within last 2 years)
        if check_opt_out_status(agreement_number, channel):
            print(f"      🛑 Skipped {agreement_number}: customer opted out")
            skipped_opt_out += 1
            continue
        
        # Preflight pressure check
        pressure_flag = get_pressure_flag(agreement_id)
        print(f"      Pressure: {pressure_flag or 'none'}")
        
        # Handle red pressure
        if pressure_flag == 'red':
            has_override = check_campaign_override(agreement_id, batch_id)
            if not has_override:
                print(f"      ⛔ Skipped {agreement_number}: pressure=red (no override)")
                skipped_red += 1
                continue
            else:
                print(f"      ✓ Proceeding due to override")
        
        # Handle amber pressure
        elif pressure_flag == 'amber':
            print(f"      ⚠️  Warning: amber pressure")
            warned_amber += 1
        
        # Get customer name
        first_name = get_customer_name(agreement_id)
        print(f"      Customer: {first_name}")
        
        # Render message
        message_text = render_template(template_body, first_name, tag)
        print(f"      Message: {message_text[:60]}...")
        
        # Log to contact_history
        log_message(
            agreement_ref=agreement_id,
            channel=channel,
            direction='outbound',
            message_text=message_text,
            intent_label='campaign_outreach',
            template_slug='offer_intro',
            source='ai',
            model=None
        )
        
        # Mark as done
        mark_pool_item_done(pool_id)
        
        sent_count += 1
        print(f"      ✅ Sent and logged")
        
        # Throttle between messages (except after last one)
        if idx < total:
            delay = random.randint(throttle_min, throttle_max)
            print(f"      ⏳ Sleeping {delay}s...")
            time_module.sleep(delay)
    
    # Summary
    print("\n" + "=" * 80)
    print(f"✨ SUMMARY")
    print("=" * 80)
    print(f"Sent: {sent_count}")
    print(f"Skipped (opted out): {skipped_opt_out}")
    print(f"Skipped (red pressure, no override): {skipped_red}")
    print(f"Warned (amber pressure): {warned_amber}")
    print(f"Duplicates: 0")
    print("=" * 80)


def main():
    """CLI entry point."""
    parser = argparse.ArgumentParser(
        description='Send templated campaign messages to queued customers'
    )
    parser.add_argument(
        '--tag',
        required=True,
        help='Campaign batch tag (e.g., A5-Upgrade-Nov-Wk2)'
    )
    parser.add_argument(
        '--channel',
        default='whatsapp',
        help='Communication channel (default: whatsapp)'
    )
    parser.add_argument(
        '--limit',
        type=int,
        default=25,
        help='Maximum messages to send (default: 25)'
    )
    parser.add_argument(
        '--throttle-min',
        type=int,
        default=5,
        help='Minimum throttle seconds (default: 5)'
    )
    parser.add_argument(
        '--throttle-max',
        type=int,
        default=15,
        help='Maximum throttle seconds (default: 15)'
    )
    parser.add_argument(
        '--ignore-window',
        action='store_true',
        help='Skip UK time window check and startup jitter (keeps throttling and pressure checks)'
    )
    
    args = parser.parse_args()
    
    try:
        run(args.tag, args.channel, args.limit, args.throttle_min, args.throttle_max, args.ignore_window)
    except Exception as e:
        print(f"\n❌ Fatal error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()
