# autonomous.py

import random
import time
import json
import os
from datetime import datetime
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from discord import Status
from discord import TextChannel
from difflib import SequenceMatcher
import globals
from utils.llm import query_ollama
from utils.moods import MOOD_EMOJIS
from utils.twitter_fetcher import fetch_miku_tweets
from utils.image_handling import analyze_image_with_qwen, download_and_encode_image

scheduler = AsyncIOScheduler()
_last_autonomous_messages = []  # rotating buffer of last general messages
MAX_HISTORY = 10
_last_user_engagements = {}  # user_id -> timestamp

LAST_SENT_TWEETS_FILE = "memory/last_sent_tweets.json"
LAST_SENT_TWEETS = []

def setup_autonomous_speaking():
    scheduler.add_job(miku_autonomous_tick, "interval", minutes=10)
    scheduler.add_job(miku_detect_and_join_conversation, "interval", minutes=3)
    scheduler.start()
    print("🤖 Autonomous Miku is active!")

async def miku_autonomous_tick(action_type="general", force=False, force_action=None):
    if not force and random.random() > 0.2:  # 20% chance to act
        return

    if force_action:
        action_type = force_action
    else:
        action_type = random.choice(["general", "engage_user", "share_tweet"])
    
    if action_type == "general":
        await miku_say_something_general()
    elif action_type == "engage_user":
        await miku_engage_random_user()
    else:
        await share_miku_tweet()

async def miku_say_something_general():
    channel = globals.client.get_channel(globals.AUTONOMOUS_CHANNEL_ID)
    if not channel:
        print("⚠️ Autonomous channel not found.")
        return

    mood = globals.CURRENT_MOOD_NAME
    time_of_day = get_time_of_day()
    emoji = MOOD_EMOJIS.get(mood, "")

    history_summary = "\n".join(f"- {msg}" for msg in _last_autonomous_messages[-5:]) if _last_autonomous_messages else "None yet."

    prompt = (
        f"Miku is feeling {mood}. It's currently {time_of_day}. "
        f"Write a short, natural message that Miku might say out of the blue in a chat. "
        f"She might greet everyone, make a cute observation, ask a silly question, or say something funny. "
        f"Make sure it feels casual and spontaneous, like a real person might say.\n\n"
        f"Here are some things Miku recently said, do not repeat them or say anything too similar:\n{history_summary}"
    )

    for attempt in range(3):  # retry up to 3 times if message is too similar
        message = await query_ollama(prompt, user_id=f"miku-general-{int(time.time())}")
        if not is_too_similar(message, _last_autonomous_messages):
            break
        print("🔁 Response was too similar to past messages, retrying...")
    
    try:
        await channel.send(message)
        print(f"💬 Miku said something general in #{channel.name}")
    except Exception as e:
        print(f"⚠️ Failed to send autonomous message: {e}")

async def miku_engage_random_user():
    guild = globals.client.get_guild(globals.TARGET_GUILD_ID)
    if not guild:
        print("⚠️ Target guild not found.")
        return

    channel = globals.client.get_channel(globals.AUTONOMOUS_CHANNEL_ID)
    if not channel:
        print("⚠️ Autonomous channel not found.")
        return

    members = [
        m for m in guild.members
        if m.status in {Status.online, Status.idle, Status.dnd} and not m.bot
    ]
    
    time_of_day = get_time_of_day()

    # Include the invisible user except during late night
    specific_user_id = 214857593045254151  # Your invisible user's ID
    specific_user = guild.get_member(specific_user_id)
    if specific_user:
        if specific_user.status != Status.offline or "late night" not in time_of_day:
            if specific_user not in members:
                members.append(specific_user)

    if not members:
        print("😴 No available members to talk to.")
        return

    target = random.choice(members)

    now = time.time()
    last_time = _last_user_engagements.get(target.id, 0)
    if now - last_time < 43200:  # 12 hours in seconds
        print(f"⏱️ Recently engaged {target.display_name}, switching to general message.")
        await miku_say_something_general()
        return

    activity_name = None
    if target.activities:
        for a in target.activities:
            if hasattr(a, 'name') and a.name:
                activity_name = a.name
                break

    mood = globals.CURRENT_MOOD_NAME
    emoji = MOOD_EMOJIS.get(mood, "")

    is_invisible = target.status == Status.offline
    display_name = target.display_name

    prompt = (
        f"Miku is feeling {mood} {emoji} during the {time_of_day}. "
        f"She notices {display_name}'s current status is {target.status.name}. "
    )

    if is_invisible:
        prompt += (
            f"Miku suspects that {display_name} is being sneaky and invisible 👻. "
            f"She wants to playfully call them out in a fun, teasing, but still affectionate way. "
        )
    elif activity_name:
        prompt += (
            f"They appear to be playing or doing: {activity_name}. "
            f"Miku wants to comment on this and start a friendly conversation."
        )
    else:
        prompt += (
            f"Miku wants to casually start a conversation with them, maybe ask how they're doing, what they're up to, or even talk about something random with them."
        )

    prompt += (
        f"\nThe message should be short and reflect Miku’s current mood."
    )

    try:
        message = await query_ollama(prompt, user_id=f"miku-engage-{int(time.time())}")
        await channel.send(f"{target.mention} {message}")
        print(f"👤 Miku engaged {display_name}")
        _last_user_engagements[target.id] = time.time()
    except Exception as e:
        print(f"⚠️ Failed to engage user: {e}")

async def miku_detect_and_join_conversation():
    channel = globals.client.get_channel(globals.AUTONOMOUS_CHANNEL_ID)
    if not isinstance(channel, TextChannel):
        print("⚠️ Autonomous channel is invalid or not found.")
        return

    # Fetch last 20 messages (for filtering)
    try:
        messages = [msg async for msg in channel.history(limit=20)]
    except Exception as e:
        print(f"⚠️ Failed to fetch channel history: {e}")
        return

    # Filter to messages in last 10 minutes from real users (not bots)
    recent_msgs = [
        msg for msg in messages
        if not msg.author.bot
        and (datetime.now(msg.created_at.tzinfo) - msg.created_at).total_seconds() < 600
    ]

    user_ids = set(msg.author.id for msg in recent_msgs)

    if len(recent_msgs) < 5 or len(user_ids) < 2:
        # Not enough activity
        return

    if random.random() > 0.5:
        return  # 50% chance to engage

    # Use last 10 messages for context (oldest to newest)
    convo_lines = reversed(recent_msgs[:10])
    history_text = "\n".join(
        f"{msg.author.display_name}: {msg.content}" for msg in convo_lines
    )

    mood = globals.CURRENT_MOOD_NAME
    emoji = MOOD_EMOJIS.get(mood, "")

    prompt = (
        f"Miku is watching a conversation happen in the chat. Her current mood is {mood} {emoji}. "
        f"She wants to say something relevant, playful, or insightful based on what people are talking about.\n\n"
        f"Here's the conversation:\n{history_text}\n\n"
        f"Write a short reply that feels natural and adds to the discussion. It should reflect Miku’s mood and personality."
    )

    try:
        reply = await query_ollama(prompt, user_id=f"miku-chat-{int(time.time())}")
        await channel.send(reply)
        print(f"💬 Miku joined an ongoing conversation.")
    except Exception as e:
        print(f"⚠️ Failed to interject in conversation: {e}")

async def share_miku_tweet():
    channel = globals.client.get_channel(globals.AUTONOMOUS_CHANNEL_ID)
    tweets = await fetch_miku_tweets(limit=5)
    if not tweets:
        print("📭 No good tweets found.")
        return

    fresh_tweets = [t for t in tweets if t["url"] not in LAST_SENT_TWEETS]

    if not fresh_tweets:
        print("⚠️ All fetched tweets were recently sent. Reusing tweets.")
        fresh_tweets = tweets

    tweet = random.choice(fresh_tweets)

    LAST_SENT_TWEETS.append(tweet["url"])
    if len(LAST_SENT_TWEETS) > 50:
        LAST_SENT_TWEETS.pop(0)

    save_last_sent_tweets()

    # Prepare prompt
    mood = globals.CURRENT_MOOD_NAME
    emoji = MOOD_EMOJIS.get(mood, "")
    base_prompt = f"Here's a tweet from @{tweet['username']}:\n\n{tweet['text']}\n\nComment on it in a fun Miku style! Miku's current mood is {mood} {emoji}. Make sure the comment reflects Miku's mood and personality."
    
    # Optionally analyze first image
    first_img_url = tweet["media"][0]
    base64_img = await download_and_encode_image(first_img_url)
    if base64_img:
        img_desc = await analyze_image_with_qwen(base64_img)
        base_prompt += f"\n\nThe image looks like this: {img_desc}"

    miku_comment = await query_ollama(base_prompt, user_id="autonomous")

    # Post to Discord
    await channel.send(f"{tweet['url']}")
    await channel.send(miku_comment)

async def handle_custom_prompt(user_prompt: str):
    channel = globals.client.get_channel(globals.AUTONOMOUS_CHANNEL_ID)
    if not channel:
        print("⚠️ Autonomous channel not found.")
        return False

    mood = globals.CURRENT_MOOD_NAME
    emoji = MOOD_EMOJIS.get(mood, "")
    time_of_day = get_time_of_day()

    # Wrap user’s idea in Miku context
    prompt = (
        f"Miku is feeling {mood} {emoji} during the {time_of_day}. "
        f"She has been instructed to: \"{user_prompt.strip()}\"\n\n"
        f"Write a short, natural message as Miku that follows this instruction. "
        f"Make it feel spontaneous, emotionally in character, and aligned with her mood and personality. Decide if the time of day is relevant to this request or not and if it is not, do not mention it."
    )

    try:
        message = await query_ollama(prompt, user_id=f"manual-{int(time.time())}")
        await channel.send(message)
        print("🎤 Miku responded to custom prompt.")
        _last_autonomous_messages.append(message)
        return True
    except Exception as e:
        print(f"❌ Failed to send custom autonomous message: {e}")
        return False

def load_last_sent_tweets():
    global LAST_SENT_TWEETS
    if os.path.exists(LAST_SENT_TWEETS_FILE):
        try:
            with open(LAST_SENT_TWEETS_FILE, "r", encoding="utf-8") as f:
                LAST_SENT_TWEETS = json.load(f)
        except Exception as e:
            print(f"⚠️ Failed to load last sent tweets: {e}")
            LAST_SENT_TWEETS = []
    else:
        LAST_SENT_TWEETS = []

def save_last_sent_tweets():
    try:
        with open(LAST_SENT_TWEETS_FILE, "w", encoding="utf-8") as f:
            json.dump(LAST_SENT_TWEETS, f)
    except Exception as e:
        print(f"⚠️ Failed to save last sent tweets: {e}")

def get_time_of_day():
    hour = datetime.now().hour + 3
    if 5 <= hour < 12:
        return "morning"
    elif 12 <= hour < 18:
        return "afternoon"
    elif 18 <= hour < 22:
        return "evening"
    return "late night. Miku wonders if anyone is still awake"

def is_too_similar(new_message, history, threshold=0.85):
    for old in history:
        ratio = SequenceMatcher(None, new_message.lower(), old.lower()).ratio()
        if ratio > threshold:
            return True
    return False
