import os
import json
import re
from groq import Groq
from dotenv import load_dotenv
import sys

load_dotenv()

client = Groq(api_key=os.getenv("GROQ_API_KEY"))

# MODEL_VERSION = "llama-3.3-70b-versatile"

MODEL_VERSIONS = [
    "llama-3.3-70b-versatile",
    "llama-3.1-8b-instant",
    "gemma2-9b-it"
]

PROMPT_VERSION_SOCIAL = "social_v1"
PROMPT_VERSION_INTERNAL = "internal_v1"

### testing code to list models and exit

# models = client.models.list()
# for m in models.data:
#     print(m.id)
# sys.exit()

# def safe_json_parse(text):
#     try:
#         return json.loads(text)
#     except:
#         match = re.search(r"\[.*\]", text, re.DOTALL)
#         if match:
#             return json.loads(match.group())
#         raise ValueError("No valid JSON found")

def safe_json_parse(text):
    """
    Strict JSON parser with fallback extraction.
    """

    try:
        return json.loads(text)

    except Exception:
        try:
            match = re.search(r"\[.*\]", text, re.DOTALL)
            if match:
                return json.loads(match.group())
        except Exception:
            pass

    return []

#MODEL FALLBACK 
def execute_with_fallback(prompt, temperature=0.2):

    last_error = None

    for model in MODEL_VERSIONS:

        try:

            print(f"[LLM] Trying model: {model}")

            response = client.chat.completions.create(
                model=model,
                messages=[{
                    "role": "user",
                    "content": prompt
                }],
                temperature=temperature
            )

            content = response.choices[0].message.content.strip()

            parsed = safe_json_parse(content)

            return parsed, model

        except Exception as e:

            error_text = str(e).lower()

            if "rate limit" in error_text:
                print(f"[RATE LIMIT] model={model}")

            else:
                print(f"[LLM ERROR] model={model} error={e}")

            last_error = e

            continue

    raise last_error

# #CALL LLM -- old SINGLE MODEL VERSION
# def call_llm(prompt, temperature=0.2):

#     try:
#         response = client.chat.completions.create(
#             model=MODEL_VERSION,
#             messages=[{"role": "user", "content": prompt}],
#             temperature=temperature
#         )

#         content = response.choices[0].message.content.strip()

#         return safe_json_parse(content)

#     except Exception as e:
#         print(f"[LLM ERROR] {e}")
#         return []
    
#CALL LLM -- NEW with fallback across multiple models
def call_llm(prompt, temperature=0.2):

    try:
        parsed, used_model = execute_with_fallback(prompt, temperature)
        print(f"[LLM SUCCESS] model={used_model}")

        return parsed, used_model

    except Exception as e:
        print(f"[LLM ERROR] {e}")

        return [], None
    

# #  SOCIAL COMMENTS ANALYSIS WITH LLM-HAS DEBATE - BATCH VERSION 
def analyze_social_comments_batch(comments_batch, question, option_a, option_b):
    formatted_comments = "\n".join(
        [f"{i+1}. {c}" for i, c in enumerate(comments_batch)]
    )

    prompt = f"""
You are a strict JSON generator.

TASK:
Classify comments in a debate context.

DEBATE:
Question: {question}
Option A: {option_a}
Option B: {option_b}

COMMENTS:
{formatted_comments}

RULES:
- Return ONLY valid JSON array
- No explanation
- No markdown
- No extra text
- Output MUST match schema exactly
- For "side": always output the exact text of Option A or Option B — NEVER output "Neutral" or "A" or "B"
- If a comment seems neutral, pick the option it leans toward most

OUTPUT FORMAT:
[
  {{
    "index": 1,
    "side": "exact text of Option A or Option B",
    "confidence": 0.0-1.0,
    "sentiment": "Positive|Negative|Neutral",
    "emotion": "anger|joy|sadness|fear|surprise|disgust|neutral|frustration|hope|sarcasm"
  }}
]
"""

    return call_llm(prompt)


# #  SOCIAL COMMENTS ANALYSIS WITH LLM- NO DEBATE- BATCH VERSION 
def analyze_comments_batch_generic(comments_batch):

    formatted_comments = "\n".join(
        [f"{i+1}. {c}" for i, c in enumerate(comments_batch)]
    )

    prompt = f"""
You are a strict JSON generator.

TASK:
Analyze generic social comments.

RULES:
- Return ONLY valid JSON array
- No explanation
- No markdown
- No extra text
- Output MUST match schema exactly

COMMENTS:
{formatted_comments}

OUTPUT FORMAT:
[
  {{
    "index": 1,
    "confidence": 0.0-1.0,
    "sentiment": "Positive|Negative|Neutral",
    "emotion": "anger|joy|sadness|fear|surprise|disgust|neutral|frustration|hope|sarcasm",
    "toxicity_score": 0.0-1.0,
    "language_code": "en"
  }}
]
"""

    return call_llm(prompt)


#  POLL COMMENTS ANALYSIS WITH LLM - BATCH VERSION (N options)
def analyze_poll_comments_batch(comments_batch, question, options):
    formatted_comments = "\n".join(
        [f"{i+1}. {c}" for i, c in enumerate(comments_batch)]
    )
    formatted_options = "\n".join([f"- {opt}" for opt in options])

    prompt = f"""
You are a strict JSON generator.

TASK:
Classify comments based on a poll question.

POLL:
Question: {question}
Options:
{formatted_options}

COMMENTS:
{formatted_comments}

RULES:
- Return ONLY valid JSON array
- No explanation
- No markdown
- No extra text
- Output MUST match schema exactly
- For "side": always output the exact text of one of the options above — pick the closest match

OUTPUT FORMAT:
[
  {{
    "index": 1,
    "side": "exact text of one poll option",
    "confidence": 0.0-1.0,
    "sentiment": "Positive|Negative|Neutral",
    "emotion": "anger|joy|sadness|fear|surprise|disgust|neutral|frustration|hope|sarcasm"
  }}
]
"""

    return call_llm(prompt)


#  INTERNAL COMMENTS ANALYSIS WITH LLM - BATCH VERSION

def analyze_internal_comments_batch(comments_batch):

    formatted_comments = "\n".join(
        [f"{i+1}. {c}" for i, c in enumerate(comments_batch)]
    )

    prompt = f"""
You are a strict JSON generator.

TASK:
Analyze internal user comments.

COMMENTS:
{formatted_comments}

RULES:
- Return ONLY valid JSON array
- No explanation
- No markdown
- No extra text
- Output MUST match schema exactly

FORMAT:
[
  {{
    "index": 1,
    "confidence": 0.0-1.0,
    "sentiment": "Positive|Negative|Neutral",
    "emotion": "anger|joy|sadness|fear|surprise|disgust|neutral|frustration|hope|sarcasm",
    "toxicity": 0.0-1.0,
    "language_code": "en",
    "impact_score": 0.0-1.0
  }}
]
"""

    return call_llm(prompt)

## HELPERS

# def get_model_version():
#     return MODEL_VERSION

def get_social_prompt_version():
    return PROMPT_VERSION_SOCIAL

def get_internal_prompt_version():
    return PROMPT_VERSION_INTERNAL
    

## OLDER VERSION - COMMENT BY COMMENT (SLOWER)
# def classify_comment_llm(comment, question, option_a, option_b):
#     prompt = f"""
# You are analyzing a debate comment.

# Debate Question:
# {question}

# Option A:
# {option_a}

# Option B:
# {option_b}

# Comment:
# {comment}

# Return ONLY valid JSON with:
# - side: "A", "B", or "Neutral"
# - confidence: float (0 to 1)
# - sentiment: "Positive", "Negative", or "Neutral"
# - emotion: one of [anger, joy, sadness, fear, surprise, disgust, neutral, frustration, hope, sarcasm, confusion]

# Do not explain anything. Only JSON.
# """

#     try:
#         response = client.chat.completions.create(
#             model=MODEL,
#             messages=[
#                 {"role": "user", "content": prompt}
#             ],
#             temperature=0.2  # low = consistent outputs
#         )

#         content = response.choices[0].message.content.strip()

#         # Try parsing JSON
#         # parsed = json.loads(content)
#         parsed = safe_json_parse(content)

#         return {
#             "side": parsed.get("side", "Neutral"),
#             "confidence": float(parsed.get("confidence", 0.5)),
#             "sentiment": parsed.get("sentiment", "Neutral"),
#             "emotion": parsed.get("emotion", "neutral"),
#             "raw": content
#         }

#     except Exception as e:
#         print("LLM ERROR:", e)
#         return {
#             "side": "Neutral",
#             "confidence": 0.5,
#             "sentiment": "Neutral",
#             "emotion": "neutral",
#             "raw": None
#         }