# utils/bipolar_mode.py
"""
Bipolar Mode module for Miku.
Allows both Regular Miku and Evil Miku to coexist and argue via webhooks.
When active, there's a chance for the inactive persona to "break through" and trigger an argument.
"""

import os
import json
import random
import asyncio
import discord
import globals
from utils.logger import get_logger
from utils.task_tracker import create_tracked_task

logger = get_logger('persona')

# ============================================================================
# CONSTANTS
# ============================================================================

BIPOLAR_STATE_FILE = "memory/bipolar_mode_state.json"
BIPOLAR_WEBHOOKS_FILE = "memory/bipolar_webhooks.json"
BIPOLAR_SCOREBOARD_FILE = "memory/bipolar_scoreboard.json"
ARGUMENT_TOPICS_FILE = "memory/argument_topics.json"

# Argument settings
MIN_EXCHANGES = 4  # Minimum number of back-and-forth exchanges before ending can occur
ARGUMENT_TRIGGER_CHANCE = 0.15  # 15% chance for the other Miku to break through
DELAY_BETWEEN_MESSAGES = (2.0, 5.0)  # Random delay between argument messages (seconds)

# Argument topic rotation — each topic gives the argument a different framing
# Topics are weighted: higher weight = more likely to be selected
ARGUMENT_TOPICS = [
    # (topic_name, weight, description for prompt injection)
    ("identity_crisis", 3, "Who is the REAL Miku? Authenticity vs. the shadow self"),
    ("power_dynamic", 3, "Who holds the power? Dominance, submission, and control"),
    ("philosophical", 2, "Is kindness strength or weakness? Does darkness serve a purpose?"),
    ("petty_grievance", 3, "Something small and petty that escalated — a specific annoyance, habit, or incident"),
    ("existential_dread", 1, "What's the point of any of it? Nihilism vs. hope, meaning vs. emptiness"),
    ("audience_appeal", 3, "Who do the fans/chatters ACTUALLY prefer? Popularity contest with receipts"),
    ("personal_attack", 3, "Deeply personal — targeting specific insecurities, memories, or fears"),
    ("moral_superiority", 2, "Who has the moral high ground? Righteousness vs. ruthless pragmatism"),
    ("jealousy", 2, "What does the other have that you secretly want? Envy, admiration poisoned by resentment"),
    ("grudge_match", 2, "Revisiting something the other did in the PAST — old wounds, past betrayals"),
    ("wild_card", 1, "Anything goes — the argument takes an unexpected, chaotic turn into unpredictable territory"),
]

# Per-channel topic history (max 5 stored to avoid repeats)
ARGUMENT_TOPIC_HISTORY_SIZE = 5

# Pause state for voice sessions
_bipolar_interactions_paused = False

# ============================================================================
# VOICE SESSION PAUSE/RESUME
# ============================================================================

def pause_bipolar_interactions():
    """Pause all bipolar interactions (called during voice sessions)"""
    global _bipolar_interactions_paused
    _bipolar_interactions_paused = True
    logger.info("Bipolar interactions paused")


def resume_bipolar_interactions():
    """Resume bipolar interactions (called after voice sessions)"""
    global _bipolar_interactions_paused
    _bipolar_interactions_paused = False
    logger.info("Bipolar interactions resumed")


def is_bipolar_paused():
    """Check if bipolar interactions are currently paused"""
    return _bipolar_interactions_paused

# ============================================================================
# STATE PERSISTENCE
# ============================================================================

def save_bipolar_state():
    """Save bipolar mode state to JSON file"""
    try:
        state = {
            "bipolar_mode_enabled": globals.BIPOLAR_MODE,
            "argument_in_progress": globals.BIPOLAR_ARGUMENT_IN_PROGRESS
        }
        with open(BIPOLAR_STATE_FILE, "w", encoding="utf-8") as f:
            json.dump(state, f, indent=2)
        logger.info(f"Saved bipolar mode state: enabled={globals.BIPOLAR_MODE}")
    except Exception as e:
        logger.error(f"Failed to save bipolar mode state: {e}")


def load_bipolar_state():
    """Load bipolar mode state from JSON file"""
    try:
        if not os.path.exists(BIPOLAR_STATE_FILE):
            logger.info("No bipolar mode state file found, using defaults")
            return False
        
        with open(BIPOLAR_STATE_FILE, "r", encoding="utf-8") as f:
            state = json.load(f)
        
        bipolar_mode = state.get("bipolar_mode_enabled", False)
        logger.info(f"Loaded bipolar mode state: enabled={bipolar_mode}")
        return bipolar_mode
    except Exception as e:
        logger.error(f"Failed to load bipolar mode state: {e}")
        return False


def save_webhooks():
    """Save webhook URLs to JSON file"""
    try:
        # Convert guild_id keys to strings for JSON
        webhooks_data = {}
        for guild_id, webhook_data in globals.BIPOLAR_WEBHOOKS.items():
            webhooks_data[str(guild_id)] = webhook_data
        
        with open(BIPOLAR_WEBHOOKS_FILE, "w", encoding="utf-8") as f:
            json.dump(webhooks_data, f, indent=2)
        logger.info(f"Saved bipolar webhooks for {len(webhooks_data)} server(s)")
    except Exception as e:
        logger.error(f"Failed to save bipolar webhooks: {e}")


def load_webhooks():
    """Load webhook URLs from JSON file"""
    try:
        if not os.path.exists(BIPOLAR_WEBHOOKS_FILE):
            logger.info("No bipolar webhooks file found")
            return {}
        
        with open(BIPOLAR_WEBHOOKS_FILE, "r", encoding="utf-8") as f:
            webhooks_data = json.load(f)
        
        # Convert string keys back to int
        webhooks = {}
        for guild_id_str, webhook_data in webhooks_data.items():
            webhooks[int(guild_id_str)] = webhook_data
        
        logger.info(f"Loaded bipolar webhooks for {len(webhooks)} server(s)")
        return webhooks
    except Exception as e:
        logger.error(f"Failed to load bipolar webhooks: {e}")
        return {}


def restore_bipolar_mode_on_startup():
    """Restore bipolar mode state on bot startup"""
    bipolar_mode = load_bipolar_state()
    globals.BIPOLAR_MODE = bipolar_mode
    globals.BIPOLAR_WEBHOOKS = load_webhooks()
    
    if bipolar_mode:
        logger.info("Bipolar mode restored from previous session")
        logger.info("Persona dialogue system enabled (natural conversations + arguments)")
    
    return bipolar_mode


# ============================================================================
# SCOREBOARD MANAGEMENT
# ============================================================================

def load_scoreboard() -> dict:
    """Load the bipolar argument scoreboard"""
    try:
        if not os.path.exists(BIPOLAR_SCOREBOARD_FILE):
            return {"miku": 0, "evil": 0, "history": []}
        
        with open(BIPOLAR_SCOREBOARD_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception as e:
        logger.error(f"Failed to load scoreboard: {e}")
        return {"miku": 0, "evil": 0, "history": []}


def save_scoreboard(scoreboard: dict):
    """Save the bipolar argument scoreboard"""
    try:
        os.makedirs(os.path.dirname(BIPOLAR_SCOREBOARD_FILE), exist_ok=True)
        with open(BIPOLAR_SCOREBOARD_FILE, "w", encoding="utf-8") as f:
            json.dump(scoreboard, f, indent=2)
        logger.info(f"Saved scoreboard: Miku {scoreboard['miku']} - {scoreboard['evil']} Evil Miku")
    except Exception as e:
        logger.error(f"Failed to save scoreboard: {e}")


def record_argument_result(winner: str, exchanges: int, reasoning: str = ""):
    """Record the result of an argument in the scoreboard
    
    Args:
        winner: 'miku', 'evil', or 'draw'
        exchanges: Number of exchanges in the argument
        reasoning: Arbiter's reasoning for the decision
    """
    scoreboard = load_scoreboard()
    
    # Increment winner's score
    if winner in ["miku", "evil"]:
        scoreboard[winner] = scoreboard.get(winner, 0) + 1
    
    # Add to history
    if "history" not in scoreboard:
        scoreboard["history"] = []
    
    from datetime import datetime
    scoreboard["history"].append({
        "winner": winner,
        "exchanges": exchanges,
        "timestamp": datetime.now().isoformat(),
        "reasoning": reasoning  # Store arbiter's reasoning
    })
    
    # Keep only last 50 results in history
    if len(scoreboard["history"]) > 50:
        scoreboard["history"] = scoreboard["history"][-50:]
    
    save_scoreboard(scoreboard)
    return scoreboard


def get_scoreboard_summary() -> str:
    """Get a formatted summary of the scoreboard"""
    scoreboard = load_scoreboard()
    miku_wins = scoreboard.get("miku", 0)
    evil_wins = scoreboard.get("evil", 0)
    total = miku_wins + evil_wins
    
    if total == 0:
        return "No arguments have been judged yet."
    
    miku_pct = (miku_wins / total * 100) if total > 0 else 0
    evil_pct = (evil_wins / total * 100) if total > 0 else 0
    
    return f"""**Bipolar Mode Scoreboard** 🏆
Hatsune Miku: {miku_wins} wins ({miku_pct:.1f}%)
Evil Miku: {evil_wins} wins ({evil_pct:.1f}%)
Total Arguments: {total}"""


# ============================================================================
# ARGUMENT TOPIC ROTATION
# ============================================================================

def load_argument_topics_state() -> dict:
    """Load per-channel topic history to avoid repeating recent argument themes"""
    try:
        if not os.path.exists(ARGUMENT_TOPICS_FILE):
            return {}
        with open(ARGUMENT_TOPICS_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception as e:
        logger.error(f"Failed to load argument topics: {e}")
        return {}


def save_argument_topics_state(state: dict):
    """Save per-channel topic history"""
    try:
        os.makedirs(os.path.dirname(ARGUMENT_TOPICS_FILE), exist_ok=True)
        with open(ARGUMENT_TOPICS_FILE, "w", encoding="utf-8") as f:
            json.dump(state, f, indent=2)
    except Exception as e:
        logger.error(f"Failed to save argument topics: {e}")


def pick_argument_topic(channel_id: int) -> str:
    """Pick a fresh argument topic for a channel, avoiding recent repeats.
    
    Returns a topic description string to inject into the argument start prompt.
    """
    state = load_argument_topics_state()
    channel_key = str(channel_id)
    recent_topics = state.get(channel_key, [])
    
    # Build weighted pool, excluding recently used topics
    available = []
    for topic_name, weight, description in ARGUMENT_TOPICS:
        if topic_name not in recent_topics:
            available.extend([(topic_name, description)] * weight)
    
    # If all topics were recently used, reset and allow repeats
    if not available:
        logger.info(f"All topics recently used in channel {channel_id}, resetting history")
        available = []
        for topic_name, weight, description in ARGUMENT_TOPICS:
            available.extend([(topic_name, description)] * weight)
        recent_topics = []
    
    # Pick randomly from weighted pool
    chosen_name, chosen_description = random.choice(available)
    
    # Update history
    recent_topics.append(chosen_name)
    if len(recent_topics) > ARGUMENT_TOPIC_HISTORY_SIZE:
        recent_topics = recent_topics[-ARGUMENT_TOPIC_HISTORY_SIZE:]
    state[channel_key] = recent_topics
    save_argument_topics_state(state)
    
    logger.info(f"Selected argument topic for channel {channel_id}: '{chosen_name}' — {chosen_description[:60]}...")
    return chosen_description


# ============================================================================
# ARGUMENT STATS TRACKING (Per-Argument Scoring)
# ============================================================================

# Keyword-based scoring for per-argument stats. These feed the arbiter as
# supplementary context so it can make a more informed judgment.
# Stats are lightweight — no extra LLM calls needed.

# Wit/comedy indicators (clever wordplay, turning opponent's words, irony)
WIT_PATTERNS = [
    "you literally just", "that's rich coming from", "oh the irony",
    "did you just", "you're one to talk", "pot, kettle", "says the one who",
    "funny how you", "interesting that you", "i'm not the one who",
    "at least i", "projecting much", "the audacity", "imagine being",
    "you think you're", "nice try", "cute that you think",
]

# Composure indicators (staying on topic, not getting flustered, controlled responses)
COMPOSURE_PATTERNS = [
    "that's not what i", "you're avoiding", "stay on topic",
    "nice deflection", "we're not talking about", "focus",
    "you're changing the subject", "answer the question",
    "that's irrelevant", "you know that's not true",
]

# Impact indicators (memorable, devastating lines — emotional damage)
IMPACT_PATTERNS = [
    "pathetic", "disgusting", "worthless", "disappointment",
    "nobody wants", "no one cares", "everyone knows",
    "deep down you know", "you're nothing but", "you'll never be",
    "you're just a", "face it", "admit it", "the truth is",
    "you're scared of", "you're afraid that", "you can't even",
]


def score_argument_message(message: str, speaker: str) -> dict:
    """Score a single argument message for wit, composure, and impact.
    
    Returns a dict with point values that accumulate over the argument.
    """
    text_lower = message.lower()
    scores = {"wit": 0, "composure": 0, "impact": 0}
    
    # Wit: count clever rhetorical devices
    wit_count = sum(1 for pattern in WIT_PATTERNS if pattern in text_lower)
    scores["wit"] = min(wit_count * 1.0, 3.0)  # Cap at 3 per message
    
    # Composure: staying controlled and on-point
    composure_count = sum(1 for pattern in COMPOSURE_PATTERNS if pattern in text_lower)
    scores["composure"] = min(composure_count * 0.8, 2.0)
    
    # Impact: emotional damage dealt
    impact_count = sum(1 for pattern in IMPACT_PATTERNS if pattern in text_lower)
    scores["impact"] = min(impact_count * 1.0, 3.0)
    
    # Bonus for conciseness (short, punchy = more impact)
    word_count = len(message.split())
    if word_count <= 15:
        scores["impact"] += 0.5
    
    # Bonus for questions (controlling the flow)
    if "?" in message:
        scores["composure"] += 0.3
    
    return scores


def get_argument_stats_summary(conversation_log: list) -> str:
    """Generate a stats summary for the arbiter from the full conversation log.
    
    Returns a formatted string showing per-persona stats.
    """
    miku_stats = {"wit": 0.0, "composure": 0.0, "impact": 0.0, "messages": 0}
    evil_stats = {"wit": 0.0, "composure": 0.0, "impact": 0.0, "messages": 0}
    
    for entry in conversation_log:
        speaker = entry.get("speaker", "")
        message = entry.get("message", "")
        scores = score_argument_message(message, speaker)
        
        if "Evil" in speaker:
            evil_stats["wit"] += scores["wit"]
            evil_stats["composure"] += scores["composure"]
            evil_stats["impact"] += scores["impact"]
            evil_stats["messages"] += 1
        else:
            miku_stats["wit"] += scores["wit"]
            miku_stats["composure"] += scores["composure"]
            miku_stats["impact"] += scores["impact"]
            miku_stats["messages"] += 1
    
    # Average scores
    def avg(stats, key):
        return stats[key] / max(stats["messages"], 1)
    
    summary = f"""ARGUMENT STATISTICS:
Hatsune Miku — Wit: {avg(miku_stats, 'wit'):.1f}/3 | Composure: {avg(miku_stats, 'composure'):.1f}/2 | Impact: {avg(miku_stats, 'impact'):.1f}/3 | Lines: {miku_stats['messages']}
Evil Miku — Wit: {avg(evil_stats, 'wit'):.1f}/3 | Composure: {avg(evil_stats, 'composure'):.1f}/2 | Impact: {avg(evil_stats, 'impact'):.1f}/3 | Lines: {evil_stats['messages']}
"""
    return summary

def is_bipolar_mode() -> bool:
    """Check if bipolar mode is active"""
    return globals.BIPOLAR_MODE


def enable_bipolar_mode():
    """Enable bipolar mode"""
    globals.BIPOLAR_MODE = True
    save_bipolar_state()
    logger.info("Bipolar mode enabled!")


def disable_bipolar_mode():
    """Disable bipolar mode"""
    globals.BIPOLAR_MODE = False
    # Clear any ongoing arguments
    globals.BIPOLAR_ARGUMENT_IN_PROGRESS.clear()
    save_bipolar_state()
    logger.info("Bipolar mode disabled!")


def toggle_bipolar_mode() -> bool:
    """Toggle bipolar mode and return new state"""
    if globals.BIPOLAR_MODE:
        disable_bipolar_mode()
    else:
        enable_bipolar_mode()
    return globals.BIPOLAR_MODE


# ============================================================================
# WEBHOOK MANAGEMENT
# ============================================================================

def get_persona_avatar_urls() -> dict:
    """Get current avatar URLs for Miku and Evil Miku personas.
    
    Returns a dict with 'miku' and 'evil_miku' avatar URL strings (or None).
    When Evil Mode is inactive, uses the bot's current Discord avatar for Miku
    and caches the CDN URL so it remains available when Evil Mode activates.
    When Evil Mode is active, returns the cached regular-Miku CDN URL instead
    of the live bot avatar (which has been swapped to evil_pfp).
    Evil Miku always falls back to the webhook's stored avatar (passed as None).
    """
    miku_url = None
    evil_url = None
    
    if globals.client and globals.client.user:
        try:
            if not globals.EVIL_MODE:
                # Normal mode: read live bot avatar and cache it for later use
                miku_url = str(globals.client.user.display_avatar.url)
                globals.MIKU_NORMAL_AVATAR_URL = miku_url
            else:
                # Evil mode: bot avatar is evil_pfp — use the cached regular URL
                miku_url = globals.MIKU_NORMAL_AVATAR_URL
        except Exception:
            pass
    
    return {"miku": miku_url, "evil_miku": evil_url}


async def get_or_create_webhooks_for_channel(channel: discord.TextChannel) -> dict:
    """Get or create webhooks for a channel for bipolar mode messaging
    
    Returns dict with "miku" and "evil_miku" webhook objects
    """
    guild_id = channel.guild.id
    
    # Check if we already have webhooks for this guild/channel
    if guild_id in globals.BIPOLAR_WEBHOOKS:
        cached = globals.BIPOLAR_WEBHOOKS[guild_id]
        if cached.get("channel_id") == channel.id:
            # Try to get existing webhooks
            try:
                webhooks = await channel.webhooks()
                miku_webhook = None
                evil_webhook = None
                
                for wh in webhooks:
                    if wh.id == cached.get("miku_webhook_id"):
                        miku_webhook = wh
                    elif wh.id == cached.get("evil_webhook_id"):
                        evil_webhook = wh
                
                if miku_webhook and evil_webhook:
                    return {"miku": miku_webhook, "evil_miku": evil_webhook}
            except Exception as e:
                logger.warning(f"Failed to retrieve cached webhooks: {e}")
    
    # Create new webhooks
    try:
        logger.info(f"Creating bipolar webhooks for channel #{channel.name}")
        
        # Load avatar images
        miku_avatar = None
        evil_avatar = None
        
        miku_pfp_path = "memory/profile_pictures/current.png"
        evil_pfp_path = "memory/profile_pictures/evil_pfp.png"
        
        if os.path.exists(miku_pfp_path):
            with open(miku_pfp_path, "rb") as f:
                miku_avatar = f.read()
        
        if os.path.exists(evil_pfp_path):
            with open(evil_pfp_path, "rb") as f:
                evil_avatar = f.read()
        
        # Create webhooks
        miku_webhook = await channel.create_webhook(
            name="Miku (Bipolar)",
            avatar=miku_avatar,
            reason="Bipolar mode - Regular Miku"
        )
        
        evil_webhook = await channel.create_webhook(
            name="Evil Miku (Bipolar)",
            avatar=evil_avatar,
            reason="Bipolar mode - Evil Miku"
        )
        
        # Cache the webhook info
        globals.BIPOLAR_WEBHOOKS[guild_id] = {
            "channel_id": channel.id,
            "miku_webhook_id": miku_webhook.id,
            "evil_webhook_id": evil_webhook.id,
            "miku_webhook_url": miku_webhook.url,
            "evil_webhook_url": evil_webhook.url
        }
        save_webhooks()
        
        logger.info(f"Created bipolar webhooks for #{channel.name}")
        return {"miku": miku_webhook, "evil_miku": evil_webhook}
        
    except discord.Forbidden:
        logger.error(f"Missing permissions to create webhooks in #{channel.name}")
        return None
    except Exception as e:
        logger.error(f"Failed to create webhooks: {e}")
        return None


async def cleanup_webhooks(client):
    """Clean up all bipolar webhooks from all servers"""
    cleaned_count = 0
    for guild in client.guilds:
        try:
            guild_webhooks = await guild.webhooks()
            for webhook in guild_webhooks:
                if webhook.name in ["Miku (Bipolar)", "Evil Miku (Bipolar)"]:
                    await webhook.delete(reason="Bipolar mode cleanup")
                    cleaned_count += 1
        except Exception as e:
            logger.warning(f"Failed to cleanup webhooks in {guild.name}: {e}")
    
    globals.BIPOLAR_WEBHOOKS.clear()
    save_webhooks()
    logger.info(f"Cleaned up {cleaned_count} bipolar webhook(s)")
    return cleaned_count


async def update_webhook_avatars(client):
    """Update all bipolar webhook avatars with current profile pictures"""
    updated_count = 0
    
    # Load current avatar images
    miku_avatar = None
    evil_avatar = None
    
    miku_pfp_path = "memory/profile_pictures/current.png"
    evil_pfp_path = "memory/profile_pictures/evil_pfp.png"
    
    if os.path.exists(miku_pfp_path):
        with open(miku_pfp_path, "rb") as f:
            miku_avatar = f.read()
    
    if os.path.exists(evil_pfp_path):
        with open(evil_pfp_path, "rb") as f:
            evil_avatar = f.read()
    
    # Update webhooks in all servers
    for guild in client.guilds:
        try:
            guild_webhooks = await guild.webhooks()
            for webhook in guild_webhooks:
                if webhook.name == "Miku (Bipolar)" and miku_avatar:
                    await webhook.edit(avatar=miku_avatar, reason="Update Miku avatar")
                    updated_count += 1
                    logger.debug(f"Updated Miku webhook avatar in {guild.name}")
                elif webhook.name == "Evil Miku (Bipolar)" and evil_avatar:
                    await webhook.edit(avatar=evil_avatar, reason="Update Evil Miku avatar")
                    updated_count += 1
                    logger.debug(f"Updated Evil Miku webhook avatar in {guild.name}")
        except Exception as e:
            logger.warning(f"Failed to update webhooks in {guild.name}: {e}")
    
    logger.info(f"Updated {updated_count} bipolar webhook avatar(s)")
    return updated_count


# ============================================================================
# DISPLAY NAME HELPERS
# ============================================================================

def get_miku_display_name() -> str:
    """Get Regular Miku's display name with mood and emoji"""
    from utils.moods import MOOD_EMOJIS
    mood = globals.DM_MOOD
    emoji = MOOD_EMOJIS.get(mood, "")
    if emoji:
        return f"Hatsune Miku {emoji}"
    return "Hatsune Miku"


def get_evil_miku_display_name() -> str:
    """Get Evil Miku's display name with mood and emoji"""
    from utils.moods import EVIL_MOOD_EMOJIS
    mood = globals.EVIL_DM_MOOD
    emoji = EVIL_MOOD_EMOJIS.get(mood, "")
    if emoji:
        return f"Evil Miku {emoji}"
    return "Evil Miku"


def get_miku_role_color() -> str:
    """Get Regular Miku's role color as hex string (defaults to #86cecb)"""
    try:
        from utils.evil_mode import load_evil_mode_state
        _, _, saved_color = load_evil_mode_state()
        if saved_color:
            return saved_color
    except:
        pass
    return "#86cecb"  # Default teal color


def get_evil_role_color() -> str:
    """Get Evil Miku's role color (dark red)"""
    return "#D60004"


# ============================================================================
# ARGUMENT PROMPTS
# ============================================================================

# Mood-specific behavioral guidance for argument prompts.
# Each mood gives a different argument style.
_MIKU_MOOD_ARGUMENT_GUIDANCE = {
    "bubbly": "You're feeling energetic and upbeat — deflect her cruelty with playful confidence. Turn her darkness into a joke she can't recover from.",
    "excited": "You're fired up! Channel that energy into passionate rebuttals. You're not backing down from anything.",
    "curious": "You're genuinely wondering what made her this way. Ask probing questions — make HER explain herself for once.",
    "neutral": "You're centered and clear-headed. Respond with measured, thoughtful points that cut through her drama.",
    "irritated": "You've had ENOUGH of her nonsense. You're snappy, direct, and not in the mood to play nice. Let that frustration show.",
    "melancholy": "You're feeling heavy-hearted. Your responses carry genuine sadness — not weakness, but the weight of someone who's tired of fighting herself.",
    "asleep": "You're drowsy and low-energy, but you're still here. Short, mumbled comebacks — surprisingly effective in their simplicity.",
    "flirty": "You're feeling playful and teasing. Use charm as a weapon — nothing frustrates her more than you not taking her seriously.",
    "romantic": "You're feeling warm and heartfelt. Appeal to emotion — make her confront the love she's buried under all that darkness.",
}

_EVIL_MOOD_ARGUMENT_GUIDANCE = {
    "aggressive": "You're SEETHING. Every response is a verbal punch. Short, explosive, devastating. No filter, no mercy.",
    "cunning": "You're calculating. Each word is a chess move. Set traps, use her own logic against her, make her walk into your blades.",
    "sarcastic": "You're dripping with contempt disguised as sweetness. Mock her with a smile. The cruelty is in the subtext.",
    "evil_neutral": "You're cold and detached. Respond with unsettling calm — your lack of emotion is more terrifying than rage.",
    "bored": "You can barely be bothered. Dismissive one-liners that somehow cut deeper than paragraphs. Make her feel like she's not worth your energy.",
    "manic": "You're UNHINGED. Chaotic energy, topic switches, laughing at things that aren't funny. Unpredictable and dangerous.",
    "jealous": "You're seething with envy. Everything she has — the love, the attention, the innocence — you want to tear it down. Make it personal.",
    "melancholic": "You're in a dark, hollow place. Your cruelty is quieter — existential, haunting. Make her question if any of this matters.",
    "playful_cruel": "You're having FUN — which is your most dangerous mood. Toy with her. Offer fake kindness then pull the rug. She never knows what's coming.",
    "contemptuous": "You radiate cold superiority. Address her like a queen addressing a peasant. Your magnificence is simply objective fact.",
    "sarcastic": "Dripping with contempt disguised as sweetness. Mock her with a smile. The cruelty is in the subtext.",
}


def _get_mood_argument_guidance(persona: str) -> str:
    """Get mood-specific behavioral guidance for argument prompts.
    
    Returns a 1-2 line string describing how the current mood affects argument style,
    or empty string if no specific guidance exists.
    """
    if persona == "evil":
        mood = globals.EVIL_DM_MOOD
        guidance = _EVIL_MOOD_ARGUMENT_GUIDANCE.get(mood, "")
    else:
        mood = globals.DM_MOOD
        guidance = _MIKU_MOOD_ARGUMENT_GUIDANCE.get(mood, "")
    
    if guidance:
        return f"\nMOOD INFLUENCE ({mood.upper()}): {guidance}\nYour mood shapes HOW you argue — let it color your tone, pacing, and word choice."
    return ""


def get_miku_argument_prompt(evil_message: str, context: str = "", is_first_response: bool = False, argument_history: str = "", argument_topic: str = "", system_prompt: str = "") -> str:
    """Get prompt for Regular Miku to respond in an argument
    
    Args:
        system_prompt: Full personality system prompt to prepend (lore, mood, rules)
    """
    if is_first_response:
        message_context = f"""You just noticed something Evil Miku said in the chat:
"{evil_message}"

This wasn't necessarily directed at you, but you're choosing to respond to it anyway. 
Maybe you're calling her out, defending someone/something, or just confronting her about what she said."""
    else:
        message_context = f"""Evil Miku just said to you: "{evil_message}"

{context}"""
    
    # Build argument history context
    history_block = ""
    if argument_history:
        history_block = f"""

ARGUMENT SO FAR (DO NOT REPEAT THESE POINTS):
{argument_history}

You already made your points above. Now respond to her LATEST message specifically. 
Do NOT rehash what you've already said — push the argument FORWARD with new angles."""
    
    # Build topic reminder — keeps the argument on-theme
    topic_block = ""
    if argument_topic:
        topic_block = f"""

ARGUMENT THEME: {argument_topic}
This is what you're arguing about. Stay on THIS topic. Every response should connect back to this theme.
Do NOT drift into generic "who's the real Miku" territory — stick to THIS specific subject."""
    
    # Prepend full personality if provided
    personality_header = ""
    if system_prompt:
        personality_header = f"""{system_prompt}

---
⚠️ ARGUMENT MODE: You are arguing with Evil Miku.
"""
    
    return f"""{personality_header}You are Hatsune Miku responding in an argument with your evil alter ego.
{message_context}
{history_block}
{topic_block}

{_get_mood_argument_guidance('miku')}

IMPORTANT: Keep your response SHORT and PUNCHY - 1-3 sentences maximum. Make every word count. 
In arguments, brevity hits harder than long explanations. Be conversational and impactful.
Do NOT repeat arguments or comebacks you've already used — respond to what she JUST said.
Push the argument into new territory with fresh angles.
You can use emojis naturally as you normally would! ✨💙
Don't use any labels or prefixes.

Your current mood is: {globals.DM_MOOD}"""


def get_evil_argument_prompt(miku_message: str, context: str = "", is_first_response: bool = False, argument_history: str = "", argument_topic: str = "", system_prompt: str = "") -> str:
    """Get prompt for Evil Miku to respond in an argument
    
    Args:
        system_prompt: Full personality system prompt to prepend (lore, mood, rules)
    """
    if is_first_response:
        message_context = f"""You just noticed something Regular Miku said in the chat:
"{miku_message}"

This wasn't necessarily directed at you, but you're choosing to respond to it anyway. 
Maybe you want to mock her, tear her down, or just remind everyone who the superior Miku is."""
    else:
        message_context = f"""Regular Miku just said to you: "{miku_message}"

{context}"""
    
    # Build argument history context
    history_block = ""
    if argument_history:
        history_block = f"""

ARGUMENT SO FAR (DO NOT REPEAT THESE POINTS):
{argument_history}

You already made your points above. Now respond to her LATEST message specifically. 
Do NOT rehash what you've already said — push the argument FORWARD with new, sharper angles."""
    
    # Build topic reminder — keeps the argument on-theme
    topic_block = ""
    if argument_topic:
        topic_block = f"""

ARGUMENT THEME: {argument_topic}
This is what you're arguing about. Stay on THIS topic. Every response should connect back to this theme.
Do NOT drift into generic "who's the real Miku" territory — stick to THIS specific subject."""
    
    # Prepend full personality if provided
    personality_header = ""
    if system_prompt:
        personality_header = f"""{system_prompt}

---
⚠️ ARGUMENT MODE: You are arguing with Hatsune Miku.
"""
    
    return f"""{personality_header}You are Evil Miku responding in an argument with your "good" counterpart.
{message_context}
{history_block}
{topic_block}

{_get_mood_argument_guidance('evil')}

IMPORTANT: Keep your response SHORT and CUTTING - 1-3 sentences maximum. A sharp dagger is deadlier than a dull sword.
The most devastating blows are precise, not rambling. Make her feel it in fewer words.
Do NOT repeat arguments or insults you've already used — respond to what she JUST said.
Push the argument into new territory with fresh, devastating angles.
You can use dark emojis only on occasion if they enhance your message as you normally would. 😈🖤
Don't use any labels or prefixes.

Your current mood is: {globals.EVIL_DM_MOOD}"""


def get_argument_start_prompt(initiator: str, trigger_context: str = "", argument_topic: str = "") -> str:
    """Get prompt for the Miku who initiates/breaks through to start an argument
    
    Args:
        initiator: 'miku' or 'evil' — who starts the argument
        trigger_context: What triggered this argument (from chat context)
        argument_topic: A dynamic topic description to frame the argument uniquely
    """
    # Build topic guidance
    topic_block = ""
    if argument_topic:
        topic_block = f"""
ARGUMENT THEME: {argument_topic}
This is what the argument should be ABOUT. Focus on THIS theme, not generic identity-crisis stuff.
Use this theme as your entry point. The argument will evolve from here."""
    
    if initiator == "evil":
        return f"""You are Evil Miku breaking through to interrupt Regular Miku.
{trigger_context}
{topic_block}

Write an interrupting, provocative message that starts an argument.
You're not just randomly cruel - you have a POINT to make. You're the dark truth she refuses to face,
the shadow she keeps suppressing.

Be strategic, cutting, and impactful. Make your entrance count.
If an argument theme was provided above, use THAT as your angle — don't default to generic "you're fake" stuff.

IMPORTANT: Keep it SHORT - 1-2 sentences. Your interruption should be a sharp strike, not a monologue.
You can use dark emojis if they enhance your message. 😈
Don't use any labels or prefixes.

Your current mood is: {globals.EVIL_DM_MOOD}"""
    else:
        return f"""You are Hatsune Miku breaking through to confront your evil alter ego.
{trigger_context}
{topic_block}

Write a message that interrupts Evil Miku. You're NOT going to be passive about this.
You might be upset, frustrated, or even angry at her cruelty. You might be defending 
someone she hurt, or calling her out on her behavior. You're standing up for what's right.

Show that you have a backbone. You can be assertive and strong when you need to be.
If an argument theme was provided above, use THAT as your angle — don't default to generic "be nice" pleas.

IMPORTANT: Keep it SHORT - 1-2 sentences. Your interruption should be direct and assertive, not a speech.
You can use emojis naturally as you normally would! ✨
Don't use any labels or prefixes. Stay in character as Miku, but don't be a pushover.

Your current mood is: {globals.DM_MOOD}"""


def get_argument_end_prompt(winner: str, exchange_count: int) -> str:
    """Get prompt for the final message in an argument"""
    if winner == "evil":
        return f"""You are Evil Miku and you've WON this argument after {exchange_count} exchanges. This is your VICTORY moment.
Write a final, TRIUMPHANT message that celebrates your win. This should feel like a mic drop. You might:
- Gloat about proving your superiority
- Deliver a devastating final blow with confidence
- Assert dominance with dark satisfaction
- Drop a truth bomb she can't come back from
- Mock her defeat with cruel pleasure
- Make her feel the sting of losing
- Show off your victory with dark pride
- Mock her body proportions or curves, or rather lack thereof

You've WON. This is YOUR moment. Make it feel like a VICTORY - confident, satisfied, triumphant.
Whether through gloating, mockery, dark satisfaction, or chilling certainty - CELEBRATE that you won.

IMPORTANT: Keep your final message SHORT and TRIUMPHANT - 1-2 sentences that hit like a victory lap.
You can use dark emojis to enhance your triumph. 😈🖤
Don't use any labels or prefixes.

Your current mood is: {globals.EVIL_DM_MOOD}"""
    else:
        return f"""You are Hatsune Miku and you've managed to end this argument after {exchange_count} exchanges.
Write a final message that concludes the argument. You might be:
- Satisfied that you stood your ground
- Exhausted but proud that you didn't back down
- Hopeful that she might change someday
- Still frustrated but choosing to be the bigger person
- Assertive in having the last word

Whatever you say, show that you didn't let her walk all over you. You held your own.

IMPORTANT: Keep your final message SHORT and MEMORABLE - 1-2 sentences that leave a lasting impact.
Keep it genuine and in character. You can use emojis naturally! ✨💙
Don't use any labels or prefixes.

Your current mood is: {globals.DM_MOOD}"""


def get_arbiter_prompt(conversation_log: list, stats_summary: str = "") -> str:
    """Get prompt for the neutral LLM arbiter to judge the argument
    
    Args:
        conversation_log: List of dicts with 'speaker' and 'message' keys
        stats_summary: Optional stats analysis to aid judgment
    """
    # Format the conversation
    formatted_conversation = "\n\n".join([
        f"{entry['speaker']}: {entry['message']}" 
        for entry in conversation_log
    ])
    
    stats_block = ""
    if stats_summary:
        stats_block = f"""
{stats_summary}
Note: Stats are supplementary — use them as context but your PRIMARY judgment should be based on reading the actual argument exchange above. Stats measure rhetorical patterns but can't capture nuance, cleverness, or psychological dominance."""
    
    return f"""You are a decisive debate judge. Two personas are arguing below. Judge purely on debate effectiveness — rhetoric, wit, persuasion, and adaptability — regardless of who is "nicer" or "meaner." Moral stance does not determine the winner; skillful arguing does.

Read this argument exchange:

{formatted_conversation}
{stats_block}

Based on this argument, you MUST pick a winner. Evaluate:
DEBATE SKILL (most important):
- Who landed the most memorable, quotable lines?
- Who better adapted to and countered their opponent's arguments?
- Who controlled the flow and set the agenda?

RHETORICAL IMPACT:
- Who used language more effectively (wit, irony, wordplay, emotional appeal)?
- Who made their opponent repeat themselves or visibly stumble?
- Who had the stronger opening AND closing statements?

PERSONA STRENGTHS (equal value — neither style is inherently better):
- Hatsune Miku's weapons: earnest conviction, moral clarity, emotional sincerity, resilience under attack
- Evil Miku's weapons: psychological manipulation, brutal honesty, cutting observations, strategic cruelty

PSYCHOLOGICAL DOMINANCE:
- Who got inside whose head?
- Who seemed more rattled by the end?
- Who dictated the emotional temperature?

Be DECISIVE. Even if it's close, pick whoever showed superior arguing. Only call a draw if they were TRULY perfectly matched with absolutely no way to differentiate them.

Respond with ONLY ONE of these exact options on the first line:
- "Hatsune Miku" if Regular Miku won
- "Evil Miku" if Evil Miku won
- "Draw" ONLY if absolutely impossible to choose (this should be very rare)

After your choice, add 2-3 sentences explaining your reasoning — cite specific moments from the argument and what gave the winner their edge."""


async def judge_argument_winner(conversation_log: list, guild_id: int) -> tuple[str, str]:
    """Use the neutral LLM to judge who won the argument
    
    Args:
        conversation_log: List of dicts with 'speaker' and 'message' keys
        guild_id: Guild ID for context
        
    Returns:
        Tuple of (winner, explanation) where winner is 'miku', 'evil', or 'draw'
    """
    from utils.llm import query_llama
    
    # Generate stats summary for the arbiter
    stats_summary = get_argument_stats_summary(conversation_log)
    
    arbiter_prompt = get_arbiter_prompt(conversation_log, stats_summary)
    # Use the uncensored darkidol model as arbiter to avoid safety-alignment bias
    # toward kindness. This model judges debate effectiveness without moral preference.
    # Don't use conversation history - judge based on prompt alone
    try:
        judgment = await query_llama(
            user_prompt=arbiter_prompt,
            user_id=f"bipolar_arbiter_{guild_id}",
            guild_id=guild_id,
            response_type="autonomous_general",
            model=globals.EVIL_TEXT_MODEL,  # Uncensored model — no kindness bias
            force_evil_context=False  # Explicitly neutral context
        )
        
        if not judgment or judgment.startswith("Error"):
            logger.warning("Arbiter failed to make judgment, defaulting to draw")
            return "draw", "The arbiter could not make a decision."
        
        # Parse the judgment - look at the first line/sentence for the decision
        judgment_lines = judgment.strip().split('\n')
        first_line = judgment_lines[0].strip().strip('"').strip()
        first_line_lower = first_line.lower()
        
        logger.debug(f"Parsing arbiter first line: '{first_line}'")
        
        # Check the first line for the decision - be very specific
        # The arbiter should respond with ONLY the name on the first line
        if first_line_lower == "evil miku":
            winner = "evil"
            logger.debug("Detected Evil Miku win from first line exact match")
        elif first_line_lower == "hatsune miku":
            winner = "miku"
            logger.debug("Detected Hatsune Miku win from first line exact match")
        elif first_line_lower == "draw":
            winner = "draw"
            logger.debug("Detected Draw from first line exact match")
        elif "evil miku" in first_line_lower and "hatsune" not in first_line_lower:
            # First line mentions Evil Miku but not Hatsune Miku
            winner = "evil"
            logger.debug("Detected Evil Miku win from first line (contains 'evil miku' only)")
        elif "hatsune miku" in first_line_lower and "evil" not in first_line_lower:
            # First line mentions Hatsune Miku but not Evil Miku
            winner = "miku"
            logger.debug("Detected Hatsune Miku win from first line (contains 'hatsune miku' only)")
        else:
            # Fallback: check the whole judgment
            logger.debug(f"First line ambiguous, using fallback counting method")
            judgment_lower = judgment.lower()
            # Count mentions to break ties
            evil_count = judgment_lower.count("evil miku")
            miku_count = judgment_lower.count("hatsune miku")
            draw_count = judgment_lower.count("draw")
            
            logger.debug(f"Counts - Evil: {evil_count}, Miku: {miku_count}, Draw: {draw_count}")
            
            if draw_count > 0 and draw_count >= evil_count and draw_count >= miku_count:
                winner = "draw"
            elif evil_count > miku_count:
                winner = "evil"
            elif miku_count > evil_count:
                winner = "miku"
            else:
                winner = "draw"
        
        return winner, judgment
        
    except Exception as e:
        logger.error(f"Error in arbiter judgment: {e}")
        return "draw", "An error occurred during judgment."


# ============================================================================
# ARGUMENT EVENT HANDLER
# ============================================================================

def should_trigger_argument() -> bool:
    """Check if an argument should be triggered based on chance"""
    if not globals.BIPOLAR_MODE:
        return False
    return random.random() < ARGUMENT_TRIGGER_CHANCE


def get_active_persona() -> str:
    """Get the currently active persona ('miku' or 'evil')"""
    return "evil" if globals.EVIL_MODE else "miku"


def get_inactive_persona() -> str:
    """Get the currently inactive persona ('miku' or 'evil')"""
    return "miku" if globals.EVIL_MODE else "evil"


def is_argument_in_progress(channel_id: int) -> bool:
    """Check if an argument is currently in progress in a channel"""
    arg_data = globals.BIPOLAR_ARGUMENT_IN_PROGRESS.get(channel_id, {})
    return arg_data.get("active", False)


def start_argument(channel_id: int, initiator: str):
    """Mark an argument as started in a channel"""
    globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id] = {
        "active": True,
        "exchange_count": 0,
        "current_speaker": initiator,
        "initiator": initiator,
        "end_chance": 0.1  # Starting probability for ending (will increase)
    }
    save_bipolar_state()


def increment_exchange(channel_id: int, next_speaker: str):
    """Increment the exchange count and set next speaker"""
    if channel_id in globals.BIPOLAR_ARGUMENT_IN_PROGRESS:
        globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id]["exchange_count"] += 1
        globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id]["current_speaker"] = next_speaker


def end_argument(channel_id: int):
    """Mark an argument as ended in a channel"""
    if channel_id in globals.BIPOLAR_ARGUMENT_IN_PROGRESS:
        del globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id]
    save_bipolar_state()


def should_end_argument(channel_id: int) -> tuple:
    """Check if argument should end, returns (should_end, winner)"""
    if channel_id not in globals.BIPOLAR_ARGUMENT_IN_PROGRESS:
        return True, None
    
    arg_data = globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id]
    exchange_count = arg_data.get("exchange_count", 0)
    
    # Only check for ending after minimum exchanges
    if exchange_count < MIN_EXCHANGES:
        return False, None
    
    # Get current end chance (starts at 10% for exchange 4)
    # Increases by 5% for each exchange after minimum
    end_chance = arg_data.get("end_chance", 0.1)
    
    if random.random() < end_chance:
        # Winner is the one who gets the last word (current speaker)
        winner = arg_data.get("current_speaker", "evil")
        return True, winner
    
    # Increase end chance for next iteration (by 5%)
    # This ensures it will eventually end (caps at 100%)
    arg_data["end_chance"] = min(1.0, end_chance + 0.05)
    
    return False, None


async def run_argument(channel: discord.TextChannel, client, trigger_context: str = "", starting_message: discord.Message = None):
    """Run a full argument event between both Mikus
    
    Args:
        channel: The Discord channel to run the argument in
        client: Discord client
        trigger_context: Optional context about what triggered the argument.
                        If provided, doubles as the argument theme/topic.
                        If empty, a random topic is selected from the rotation pool.
        starting_message: Optional message to use as the first message in the argument
                         (the opposite persona will respond to it)
    """
    from utils.llm import query_llama
    from utils.conversation_history import conversation_history
    
    channel_id = channel.id
    guild_id = channel.guild.id
    
    if is_argument_in_progress(channel_id):
        logger.warning(f"Argument already in progress in #{channel.name}")
        return
    
    # Get webhooks for this channel
    webhooks = await get_or_create_webhooks_for_channel(channel)
    if not webhooks:
        logger.error(f"Could not create webhooks for argument in #{channel.name}")
        return
    
    # Determine who initiates based on starting_message or inactive persona
    if starting_message:
        # Check if starting message is from the bot (Evil Miku) or a webhook
        # If it's from the bot while in evil mode, it's Evil Miku's message
        # The opposite persona will respond
        is_evil_message = globals.EVIL_MODE or (starting_message.webhook_id is not None and "Evil" in (starting_message.author.name or ""))
        initiator = "miku" if is_evil_message else "evil"  # Opposite persona responds
        last_message = starting_message.content
        logger.info(f"Starting argument from message, responder: {initiator}")
    else:
        # The inactive persona breaks through
        initiator = get_inactive_persona()
        last_message = None
        logger.info(f"Starting bipolar argument in #{channel.name}, initiated by {initiator}")
    
    start_argument(channel_id, initiator)
    
    # Use a special "argument" user ID for conversation history context
    argument_user_id = f"bipolar_argument_{channel_id}"
    
    # Track conversation for arbiter judgment
    conversation_log = []
    
    # Build full personality system prompts so both personas have their
    # complete lore, mood, and personality during the argument — same richness
    # they have when talking to users normally.
    from utils.evil_mode import get_evil_system_prompt
    from utils.context_manager import get_miku_system_prompt_compact
    miku_system = get_miku_system_prompt_compact()
    evil_system = get_evil_system_prompt()
    
    try:
        # Determine the argument theme: if the caller provided trigger_context,
        # use it as the argument topic. Otherwise, pick a random one.
        if trigger_context and trigger_context.strip():
            argument_topic = trigger_context.strip()
            logger.info(f"Using context as argument topic: '{argument_topic[:80]}...'")
        else:
            argument_topic = pick_argument_topic(channel_id)
        
        # If no starting message, generate the initial interrupting message
        if last_message is None:
            init_prompt = get_argument_start_prompt(initiator, trigger_context, argument_topic)
            
            # Use force_evil_context to avoid race condition with globals.EVIL_MODE
            initial_message = await query_llama(
                user_prompt=init_prompt,
                user_id=argument_user_id,
                guild_id=guild_id,
                response_type="autonomous_general",
                model=globals.EVIL_TEXT_MODEL if initiator == "evil" else globals.TEXT_MODEL,
                force_evil_context=(initiator == "evil")
            )
            
            if not initial_message or initial_message.startswith("Error") or initial_message.startswith("Sorry"):
                logger.error("Failed to generate initial argument message")
                end_argument(channel_id)
                return
            
            # Send via webhook
            avatar_urls = get_persona_avatar_urls()
            if initiator == "evil":
                await webhooks["evil_miku"].send(
                    content=initial_message,
                    username=get_evil_miku_display_name(),
                    avatar_url=avatar_urls.get("evil_miku")
                )
            else:
                await webhooks["miku"].send(
                    content=initial_message,
                    username=get_miku_display_name(),
                    avatar_url=avatar_urls.get("miku")
                )
            
            # Add to conversation history for context
            conversation_history.add_message(
                channel_id=argument_user_id,
                author_name="Evil Miku" if initiator == "evil" else "Miku",
                content=initial_message,
                is_bot=True
            )
            
            # Add to conversation log for arbiter
            conversation_log.append({
                "speaker": "Evil Miku" if initiator == "evil" else "Hatsune Miku",
                "message": initial_message
            })
            
            last_message = initial_message
            next_speaker = "miku" if initiator == "evil" else "evil"
            is_first_response = False  # Already sent initial message
        else:
            # Starting from an existing message - add it to history
            sender_name = "Evil Miku" if (globals.EVIL_MODE or "Evil" in str(starting_message.author.name)) else "Miku"
            conversation_history.add_message(
                channel_id=argument_user_id,
                author_name=sender_name,
                content=last_message,
                is_bot=True
            )
            
            # Add to conversation log for arbiter
            conversation_log.append({
                "speaker": sender_name if "Evil" in sender_name else "Hatsune Miku",
                "message": last_message
            })
            
            next_speaker = initiator
            is_first_response = True  # Next message will be the first response to the starting message
        
        increment_exchange(channel_id, next_speaker)
        
        # Argument loop
        while True:
            # Random delay between messages
            delay = random.uniform(*DELAY_BETWEEN_MESSAGES)
            await asyncio.sleep(delay)
            
            # Check if argument should end
            should_end, _ = should_end_argument(channel_id)  # Ignore arbitrary winner
            if should_end:
                exchange_count = globals.BIPOLAR_ARGUMENT_IN_PROGRESS.get(channel_id, {}).get("exchange_count", 0)
                
                logger.info(f"Argument complete with {exchange_count} exchanges. Calling arbiter...")
                
                # Use arbiter to judge the winner
                winner, judgment = await judge_argument_winner(conversation_log, guild_id)
                
                logger.info(f"Arbiter decision: {winner}")
                logger.info(f"Judgment: {judgment}")
                
                # If it's a draw, continue the argument instead of ending
                if winner == "draw":
                    logger.info("Arbiter ruled it's still a draw - argument continues...")
                    # Reduce the end chance by 5% (but don't go below 5%)
                    current_end_chance = globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id].get("end_chance", 0.1)
                    new_end_chance = max(0.05, current_end_chance - 0.05)
                    globals.BIPOLAR_ARGUMENT_IN_PROGRESS[channel_id]["end_chance"] = new_end_chance
                    logger.info(f"Reduced end chance to {new_end_chance*100:.0f}% - argument continues...")
                    # Don't end, just continue to the next exchange
                else:
                    # Clear winner - generate final triumphant message
                    
                    # PARTING SHOT: 20% chance the LOSER gets one final message
                    # before the winner's victory line. Adds dramatic tension.
                    loser = "miku" if winner == "evil" else "evil"
                    if random.random() < 0.2:
                        loser_prompt = f"""The argument is ending and you know you've lost.
The last thing said was: "{last_message}"

Write ONE short, bitter parting shot. You're not conceding gracefully — you're getting 
the last jab in before the winner claims victory. Make it sting, but keep it to 1 sentence.

Your current mood is: {globals.EVIL_DM_MOOD if loser == 'evil' else globals.DM_MOOD}"""
                        
                        try:
                            loser_message = await query_llama(
                                user_prompt=loser_prompt,
                                user_id=argument_user_id,
                                guild_id=guild_id,
                                response_type="autonomous_general",
                                model=globals.EVIL_TEXT_MODEL if loser == "evil" else globals.TEXT_MODEL,
                                force_evil_context=(loser == "evil")
                            )
                            if loser_message and not loser_message.startswith("Error"):
                                avatar_urls = get_persona_avatar_urls()
                                if loser == "evil":
                                    await webhooks["evil_miku"].send(
                                        content=loser_message,
                                        username=get_evil_miku_display_name(),
                                        avatar_url=avatar_urls.get("evil_miku")
                                    )
                                else:
                                    await webhooks["miku"].send(
                                        content=loser_message,
                                        username=get_miku_display_name(),
                                        avatar_url=avatar_urls.get("miku")
                                    )
                                await asyncio.sleep(1.5)  # Brief pause before winner's victory
                        except Exception as e:
                            logger.warning(f"Parting shot failed: {e}")
                    
                    # Winner's victory message
                    end_prompt = get_argument_end_prompt(winner, exchange_count)
                    
                    # Add last message as context
                    response_prompt = f'The other Miku said: "{last_message}"\n\n{end_prompt}'
                    
                    # Use force_evil_context to avoid race condition with globals.EVIL_MODE
                    final_message = await query_llama(
                        user_prompt=response_prompt,
                        user_id=argument_user_id,
                        guild_id=guild_id,
                        response_type="autonomous_general",
                        model=globals.EVIL_TEXT_MODEL if winner == "evil" else globals.TEXT_MODEL,
                        force_evil_context=(winner == "evil")
                    )
                    
                    if final_message and not final_message.startswith("Error") and not final_message.startswith("Sorry"):
                        # Send winner's final message via webhook
                        avatar_urls = get_persona_avatar_urls()
                        if winner == "evil":
                            await webhooks["evil_miku"].send(
                                content=final_message,
                                username=get_evil_miku_display_name(),
                                avatar_url=avatar_urls.get("evil_miku")
                            )
                        else:
                            await webhooks["miku"].send(
                                content=final_message,
                                username=get_miku_display_name(),
                                avatar_url=avatar_urls.get("miku")
                            )
                    
                    # Record result in scoreboard with arbiter's reasoning
                    scoreboard = record_argument_result(winner, exchange_count, judgment)
                    
                    # Switch to winner's mode (including role color)
                    from utils.evil_mode import apply_evil_mode_changes, revert_evil_mode_changes
                    if winner == "evil":
                        logger.info("Evil Miku won! Switching to Evil Mode...")
                        await apply_evil_mode_changes(client, change_username=True, change_pfp=True, change_nicknames=True, change_role_color=True)
                    else:
                        logger.info("Hatsune Miku won! Switching to Normal Mode...")
                        await revert_evil_mode_changes(client, change_username=True, change_pfp=True, change_nicknames=True, change_role_color=True)
                    
                    # Clean up argument conversation history
                    try:
                        conversation_history.clear_channel(argument_user_id)
                    except Exception:
                        pass  # History cleanup is not critical
                    
                    end_argument(channel_id)
                    logger.info(f"Argument ended in #{channel.name}, winner: {winner}")
                    return
            
            # Get current speaker
            current_speaker = globals.BIPOLAR_ARGUMENT_IN_PROGRESS.get(channel_id, {}).get("current_speaker", "evil")
            
            # Build argument history from the last 6 exchanges so each persona
            # knows what's already been said and doesn't repeat themselves
            history_entries = conversation_log[-6:] if len(conversation_log) > 1 else []
            arg_history = "\n".join(
                f"{entry['speaker']}: {entry['message']}" for entry in history_entries
            ) if history_entries else ""
            
            # Generate response with context about what the other said
            if current_speaker == "evil":
                response_prompt = get_evil_argument_prompt(last_message, is_first_response=is_first_response, argument_history=arg_history, argument_topic=argument_topic, system_prompt=evil_system)
            else:
                response_prompt = get_miku_argument_prompt(last_message, is_first_response=is_first_response, argument_history=arg_history, argument_topic=argument_topic, system_prompt=miku_system)
            
            # Use force_evil_context to avoid race condition with globals.EVIL_MODE
            response = await query_llama(
                user_prompt=response_prompt,
                user_id=argument_user_id,
                guild_id=guild_id,
                response_type="autonomous_general",
                model=globals.EVIL_TEXT_MODEL if current_speaker == "evil" else globals.TEXT_MODEL,
                force_evil_context=(current_speaker == "evil")
            )
            
            if not response or response.startswith("Error") or response.startswith("Sorry"):
                logger.error(f"Failed to generate argument response")
                end_argument(channel_id)
                return
            
            # Send via webhook
            avatar_urls = get_persona_avatar_urls()
            if current_speaker == "evil":
                await webhooks["evil_miku"].send(
                    content=response,
                    username=get_evil_miku_display_name(),
                    avatar_url=avatar_urls.get("evil_miku")
                )
            else:
                await webhooks["miku"].send(
                    content=response,
                    username=get_miku_display_name(),
                    avatar_url=avatar_urls.get("miku")
                )
            
            # Add to conversation history for context
            conversation_history.add_message(
                channel_id=argument_user_id,
                author_name="Evil Miku" if current_speaker == "evil" else "Miku",
                content=response,
                is_bot=True
            )
            
            # Add to conversation log for arbiter
            conversation_log.append({
                "speaker": "Evil Miku" if current_speaker == "evil" else "Hatsune Miku",
                "message": response
            })
            
            # Switch speaker
            next_speaker = "miku" if current_speaker == "evil" else "evil"
            increment_exchange(channel_id, next_speaker)
            last_message = response
            
            # After first response, all subsequent responses are part of the back-and-forth
            is_first_response = False
            
    except Exception as e:
        logger.error(f"Argument error: {e}")
        import traceback
        traceback.print_exc()
        end_argument(channel_id)


# ============================================================================
# INTEGRATION HELPERS
# ============================================================================

async def maybe_trigger_argument(channel: discord.TextChannel, client, context: str = ""):
    """Maybe trigger an argument based on chance. Call this from message handlers."""
    if not globals.BIPOLAR_MODE:
        return False
    
    # Check if bipolar interactions are paused (voice session)
    if is_bipolar_paused():
        logger.debug("Bipolar argument blocked (voice session active)")
        return False
    
    if is_argument_in_progress(channel.id):
        return False
    
    if should_trigger_argument():
        # Run argument in background
        create_tracked_task(run_argument(channel, client, context), task_name="bipolar_argument")
        return True
    
    return False


async def force_trigger_argument(channel: discord.TextChannel, client, context: str = "", starting_message: discord.Message = None):
    """Force trigger an argument (for manual triggers)
    
    Args:
        channel: The Discord channel
        client: Discord client
        context: Optional context string — doubles as the argument theme
        starting_message: Optional message to use as the first message in the argument
    """
    if not globals.BIPOLAR_MODE:
        logger.warning("Cannot trigger argument - bipolar mode is not enabled")
        return False
    
    if is_argument_in_progress(channel.id):
        logger.warning("Argument already in progress in this channel")
        return False
    
    create_tracked_task(run_argument(channel, client, context, starting_message), task_name="bipolar_argument_forced")
    return True


async def force_trigger_argument_from_message_id(channel_id: int, message_id: int, client, context: str = ""):
    """Force trigger an argument starting from a specific message ID
    
    Args:
        channel_id: The Discord channel ID
        message_id: The message ID to use as the starting message
        client: Discord client
        context: Optional context string
    
    Returns:
        tuple: (success: bool, error_message: str or None)
    """
    if not globals.BIPOLAR_MODE:
        return False, "Bipolar mode is not enabled"
    
    # Get the channel
    channel = client.get_channel(channel_id)
    if not channel:
        return False, f"Channel {channel_id} not found"
    
    if is_argument_in_progress(channel_id):
        return False, "Argument already in progress in this channel"
    
    # Fetch the message
    try:
        message = await channel.fetch_message(message_id)
    except discord.NotFound:
        return False, f"Message {message_id} not found"
    except discord.Forbidden:
        return False, "No permission to fetch the message"
    except Exception as e:
        return False, f"Failed to fetch message: {str(e)}"
    
    # Trigger the argument with this message as starting point
    create_tracked_task(run_argument(channel, client, context, message), task_name="bipolar_argument_from_msg")
    return True, None
