import json
import re
import logging
from textblob import TextBlob
from app.services.ai_assessment import get_ai_assessor
logger = logging.getLogger(__name__)


class ComprehensionAssessor:
    """New, focused comprehension assessor.

    Contract (inputs/outputs):
    - Inputs: question_text (str), student_answer (str), reading_content (str)
    - Outputs: dict with keys:
      - relevance_score (int 0-100)
      - sentence_score (int 0-100)
      - grammar_score (int 0-100)
      - overall_score (int 0-100)
      - corrections: list of {original, corrected, explanation}
      - suggestions: textual suggestions
      - corrected_answer: suggested full corrected answer

    Error modes: returns conservative defaults and a human-readable suggestions field.
    """

    def __init__(self):
        self.ai = get_ai_assessor()

    def evaluate_answer(self, question_text, student_answer, reading_content):
        try:
            # Log to verify reading content is passed
            logger.info(f"ComprehensionAssessor: Evaluating with reading_content length: {len(reading_content) if reading_content else 0}")
            
            prompt = f"""
You are a warm, encouraging English teacher evaluating a BEGINNER student's comprehension answer. 
Your role is to build confidence while gently guiding improvement. 🌟

⚠️ CRITICAL: READ THE READING CONTENT BELOW FIRST! The student's answer must be evaluated based on this reading.

📚 READING CONTENT (READ THIS FIRST):
{reading_content[:2500] if reading_content else "No reading content provided"}

❓ QUESTION ASKED:
{question_text}

✍️ STUDENT'S ANSWER:
{student_answer}

🎯 EVALUATION PHILOSOPHY - BE ENCOURAGING!
This is a BEGINNER English learner. They are brave for trying! Your job is to:
1. ✅ Celebrate what they got RIGHT first
2. 🎯 Focus on COMMUNICATION SUCCESS over perfection
3. 💪 Give GENEROUS scores to build confidence
4. 🌱 Gently suggest improvements without criticism
5. 😊 Use positive, supportive language throughout

🎯 EVALUATION STEPS (FOLLOW IN ORDER):
1. READ the reading content above carefully
2. CHECK if the student's answer matches information from the reading
3. VERIFY if the answer is CORRECT based on the reading
4. If they understood the content correctly → Minimum 85-95 score (even if simple or has minor grammar!)
5. If they tried and answered something relevant → Minimum 70-80 score
6. Remember: Communication beats grammar perfection!

Return ONLY a valid JSON object with the following keys exactly:
{{
    "relevance_score": 0-100 integer,
    "sentence_score": 0-100 integer,
    "grammar_score": 0-100 integer,
    "overall_score": 0-100 integer,
    "corrections": [ {{"original": "...", "corrected": "...", "explanation": "..."}}, ... ],
    "suggestions": "...",
    "corrected_answer": "..."
}}

⚠️ CRITICAL RULES FOR CORRECTIONS (READ CAREFULLY):
1. Do NOT mark PROPER NOUNS as spelling errors (city names, person names, universities, company names, etc.)
2. Do NOT mark ABBREVIATIONS or ACRONYMS as spelling errors (LGU, MIT, USA, CEO, etc.)
3. Check the reading content first - if a word appears there, it's CORRECT
4. Only flag OBVIOUS typos like "teh" → "the", NOT proper nouns like "Lahore" or "LGU"
5. If a word is capitalized or ALL CAPS, it's probably a name/abbreviation - LEAVE IT ALONE
6. NEVER correct names of people, places, organizations, or brands mentioned in the reading

❌ EXAMPLES OF WRONG CORRECTIONS (DO NOT DO THIS):
- "Lahore" → "labor" ❌ NO! Lahore is a city name
- "LGU" → "you" ❌ NO! LGU is an abbreviation
- "MIT" → "it" ❌ NO! MIT is a university
- "Karachi" → "car" ❌ NO! Karachi is a city name
- "Google" → "goggle" ❌ NO! Google is a company name

✅ EXAMPLES OF CORRECT CORRECTIONS (DO THIS):
- "teh" → "the" ✅ (obvious typo)
- "studetn" → "student" ✅ (obvious typo)
- Missing capital at sentence start ✅ (but be gentle!)
- Missing period at sentence end ✅ (minor issue)

🎯 SCORING GUIDANCE (BE GENEROUS - FOLLOW EXACTLY):

📊 RELEVANCE SCORE (Most Important - Focus on Understanding):
- Answer shows understanding and addresses question → 90-100 ⭐ (BE GENEROUS!)
- Answer is mostly correct, slightly incomplete → 85-95 💪
- Answer shows partial understanding → 75-84 👍
- Answer tries to address question → 65-74 🌱
- If the answer correctly responds to the question, score MUST be at least 85!
- REMEMBER: Understanding the content is what matters most!

📊 GRAMMAR SCORE (Be VERY Lenient - They're Learning!):
- Perfect or 1-2 tiny errors → 95-100 ⭐
- Few small errors (missing capital, punctuation) → 88-94 💪
- Several small errors but meaning is clear → 80-87 👍
- Grammar issues but still understandable → 70-79 🌱
- DO NOT penalize for proper nouns, abbreviations, or names!
- REMEMBER: If you understand them, the grammar is good enough!

📊 SENTENCE SCORE (Coherence/Flow - Be Kind!):
- Clear and coherent answer → 92-100 ⭐
- Mostly clear with minor flow issues → 85-91 💪
- Simple but understandable → 78-84 👍
- Basic but gets the point across → 70-77 🌱

📊 OVERALL SCORE (CRITICAL - BE ENCOURAGING!):
🌟 GOLDEN RULES for Overall Score:
- Understood the content correctly → 90-100 (even if grammar/style isn't perfect!)
- If relevance ≥ 85 → Overall MUST be 88-100 
- If relevance ≥ 75 → Overall should be 80-92
- If they tried and got partial understanding → Minimum 70-80
- Grammar/style should reduce overall by MAX 5-8 points if content understanding is correct
- Remember: A correct simple answer like "Person A is doing well." deserves 90-95!

💡 ENCOURAGEMENT GUIDELINES:
1. Start feedback with what they did WELL ("Great job understanding...", "Excellent that you mentioned...")
2. Use gentle language for corrections ("You might want to...", "Consider adding...", "Try including...")
3. End with encouragement ("You're making great progress!", "Keep up the good work!")
4. Never use harsh words like "wrong", "incorrect", "poor" - use "let's improve", "we can make it better"

⚠️ CRITICAL RULES FOR AI FEEDBACK:
1. This is a BEGINNER learner - CELEBRATE THEIR EFFORT!
2. If they answered correctly, give them 90-95+ overall (simplicity is OK!)
3. Focus on what they understood, not what's missing
4. Use positive, encouraging tone in ALL feedback
5. Build confidence - they need it to keep learning!
6. Example: "Wonderful! You understood that Person A is doing well. That's exactly right! 🌟"

Provide warm, encouraging feedback that makes the student want to keep learning.
"""
            logger.info("ComprehensionAssessor: requesting AI evaluation")
            model = self.ai.get_model()
            resp = model.generate_content(prompt)
            text = getattr(resp, 'text', str(resp)).strip()
            # Extract JSON
            m = re.search(r'\{.*\}', text, re.DOTALL)
            parsed = None
            if m:
                try:
                    parsed = json.loads(m.group(0))
                except Exception:
                    parsed = None
            if not parsed:
                # Try plain parse
                try:
                    parsed = json.loads(text)
                except Exception:
                    logger.warning("ComprehensionAssessor: failed to parse AI response, using fallback formatting")
                    return self._fallback(student_answer)

            # Validate numeric fields with generous minimum scores
            def norm(v, default=75, minimum=0):
                try:
                    iv = int(v)
                    # Ensure score is between minimum and 100
                    return max(minimum, min(100, iv))
                except Exception:
                    return default

            # Get scores with generous defaults for beginners
            relevance = norm(parsed.get('relevance_score', parsed.get('content_score', 0)), default=80, minimum=0)
            sentence = norm(parsed.get('sentence_score', parsed.get('coherence_score', 0)), default=75, minimum=0)
            grammar = norm(parsed.get('grammar_score', 0), default=75, minimum=0)
            
            # Calculate overall score - be generous!
            calculated_overall = round((relevance + sentence + grammar) / 3)
            overall = norm(parsed.get('overall_score', calculated_overall), default=calculated_overall, minimum=0)
            
            # Apply encouragement boost: If they show understanding (relevance >= 80), ensure minimum overall of 85
            if relevance >= 80 and overall < 85:
                overall = 85
                logger.info(f"Applied encouragement boost: relevance {relevance} → overall adjusted to {overall}")
            # If they tried and got partial understanding (relevance >= 65), ensure minimum 70
            elif relevance >= 65 and overall < 70:
                overall = 70
                logger.info(f"Applied partial understanding boost: relevance {relevance} → overall adjusted to {overall}")

            corrections = parsed.get('corrections', parsed.get('grammar_errors', parsed.get('corrections', [])))
            if not isinstance(corrections, list):
                corrections = []

            # Make suggestions more encouraging
            suggestions = parsed.get('suggestions', parsed.get('improvements', parsed.get('suggestions', '')))
            if not suggestions or suggestions.strip() == '':
                suggestions = "Great effort! You're on the right track. Keep practicing your reading comprehension! 🌟"
            
            corrected_answer = parsed.get('corrected_answer', '')
            if not corrected_answer:
                corrected_answer = student_answer  # Keep their answer if no correction provided

            return {
                'relevance_score': relevance,
                'sentence_score': sentence,
                'grammar_score': grammar,
                'overall_score': overall,
                'corrections': corrections,
                'suggestions': suggestions,
                'corrected_answer': corrected_answer
            }
        except Exception as e:
            logger.exception(f"ComprehensionAssessor error: {e}")
            return self._fallback(student_answer)

    def _fallback(self, student_answer):
        """Encouraging fallback when AI evaluation fails - still give credit for effort!"""
        # Give generous scores for attempting the answer
        word_count = len(student_answer.split()) if student_answer else 0
        
        if word_count == 0:
            return {
                'relevance_score': 0,
                'sentence_score': 0,
                'grammar_score': 0,
                'overall_score': 0,
                'corrections': [],
                'suggestions': 'Please provide an answer to the question. You can do it! 💪',
                'corrected_answer': student_answer
            }
        
        # Give credit for any attempt - be encouraging!
        base_score = 70  # Generous base for trying
        if word_count >= 10:
            base_score = 75
        if word_count >= 20:
            base_score = 80
            
        return {
            'relevance_score': base_score,
            'sentence_score': base_score,
            'grammar_score': base_score - 5,  # Slightly lower grammar since we can't check
            'overall_score': base_score,
            'corrections': [],
            'suggestions': 'Great effort! 🌟 We had a technical issue evaluating your answer, but we can see you tried. Your teacher will review this manually. Keep up the good work!',
            'corrected_answer': student_answer
        }

    def fast_score(self, question_text, student_answer, reading_content):
        """Fast, local heuristic scorer used for immediate UI feedback.

        This is intentionally lightweight and deterministic so the UI can show
        provisional scores quickly while the full AI assessment runs in the
        background.
        Returns the same key set as evaluate_answer, plus a '_fast' flag.
        """
        try:
            sa = (student_answer or '').strip()
            if not sa:
                return {
                    'relevance_score': 0,
                    'sentence_score': 0,
                    'grammar_score': 0,
                    'overall_score': 0,
                    'corrections': [],
                    'suggestions': 'No answer provided.',
                    'corrected_answer': sa,
                    '_fast': True
                }

            # Simple token overlap for relevance
            def words(text):
                return re.findall(r"\w+", (text or '').lower())

            qwords = set(words(question_text))
            rwords = set(words(reading_content))
            swords = set(words(sa))

            if qwords:
                overlap = len(swords & qwords)
                relevance = int(min(100, (overlap / max(1, len(qwords))) * 100))
            else:
                overlap = len(swords & rwords)
                relevance = int(min(100, (overlap / max(1, len(rwords))) * 100))

            # Sentence score heuristic: reward longer, punctuated answers - BE GENEROUS!
            sentences = re.split(r'[.!?]+', sa)
            sentence_count = sum(1 for s in sentences if s.strip())
            words_count = max(1, len(re.findall(r"\w+", sa)))
            # Increased base score from 60 to 75 for encouragement
            sentence_score = 75 + int(min(25, (words_count / 15) * 25))  # Easier to reach high scores
            if sentence_count > 1:
                sentence_score = min(100, sentence_score + 5)

            # Grammar heuristic: BE LENIENT - only penalize obvious issues
            grammar_penalty = 0
            if sa and sa[0].islower():
                grammar_penalty += 3  # Reduced from 5
            if not any(p in sa for p in ".,!?;:"):
                grammar_penalty += 3  # Reduced from 5
            # repeated short word heuristic
            tokens = re.findall(r"\w+", sa.lower())
            repeats = sum(1 for i in range(1, len(tokens)) if tokens[i] == tokens[i-1])
            grammar_penalty += min(10, repeats * 3)  # Reduced penalty

            # Spelling check (lightweight, local) using TextBlob - BE CAREFUL WITH PROPER NOUNS!
            spelling_corrections = []
            spelling_errors = 0
            try:
                # Check each token for simple spelling correction differences
                for tok in tokens:
                    if len(tok) <= 2 or not tok.isalpha():
                        continue
                    # Skip if it's capitalized (likely a proper noun)
                    if tok[0].isupper():
                        continue
                    # Skip if it appears in the reading content
                    if reading_content and tok.lower() in reading_content.lower():
                        continue
                    
                    corrected = str(TextBlob(tok).correct())
                    if corrected and corrected.lower() != tok.lower():
                        spelling_errors += 1
                        spelling_corrections.append({'original': tok, 'corrected': corrected, 'explanation': 'Possible spelling correction'})
            except Exception:
                # If TextBlob fails for any reason, don't block scoring
                spelling_corrections = []
                spelling_errors = 0

            # Reduce penalty for spelling errors - be more forgiving
            grammar_penalty += min(10, spelling_errors * 2)  # Reduced from 20 and 4

            # Increased base grammar score from 30 to 70, reduced penalty effect
            grammar_score = max(70, 98 - grammar_penalty)  # More generous minimum

            # Calculate overall with generous boost
            calculated_overall = round((relevance + sentence_score + grammar_score) / 3)
            
            # Apply encouragement boost for fast scoring too!
            if relevance >= 75 and calculated_overall < 80:
                overall = 80
            elif relevance >= 60 and calculated_overall < 70:
                overall = 70
            else:
                overall = calculated_overall
            # Try to generate a short AI-style one-line feedback and weak areas using the configured AI model.
            short_feedback = ''
            weak_areas = ''
            ai_suggestions = ''
            try:
                model = self.ai.get_model()
                prompt = f"""
You are a helpful English teacher. Based on the following quick diagnostics, produce a compact JSON object with keys: "short_feedback", "weak_areas", "suggestion".
Return only valid JSON.

QUESTION: {question_text}
STUDENT_ANSWER: {sa}
DIAGNOSTICS: relevance={relevance}, sentence_score={sentence_score}, grammar_score={grammar_score}, spelling_errors={spelling_errors}

Be concise. "short_feedback" should be one short sentence describing the main problem or strength. "weak_areas" should be a short comma-separated list. "suggestion" should be one actionable improvement sentence.
"""
                resp = model.generate_content(prompt)
                text = getattr(resp, 'text', str(resp)).strip()
                m = re.search(r'\{.*\}', text, re.DOTALL)
                parsed = None
                if m:
                    try:
                        parsed = json.loads(m.group(0))
                    except Exception:
                        parsed = None
                if not parsed:
                    try:
                        parsed = json.loads(text)
                    except Exception:
                        parsed = None

                if isinstance(parsed, dict):
                    short_feedback = parsed.get('short_feedback', '')
                    weak_areas = parsed.get('weak_areas', '')
                    ai_suggestions = parsed.get('suggestion', '')
            except Exception:
                # Ignore AI failures for the fast path; fall back to heuristic messages
                short_feedback = ''
                weak_areas = ''
                ai_suggestions = ''

            # Fallback heuristics when AI not available or produced nothing
            if not short_feedback:
                # Build a simple one-line message from diagnostics
                parts = []
                if spelling_errors:
                    parts.append(f"{spelling_errors} spelling error(s)")
                if grammar_penalty > 10:
                    parts.append("grammar issues")
                if relevance < 50:
                    parts.append("relevance to the question is low")
                if not parts:
                    short_feedback = "Your answer is generally fine; full AI feedback will provide more details."
                else:
                    short_feedback = "Main issues: " + ", ".join(parts) + "."

            if not weak_areas:
                wa = []
                if spelling_errors:
                    wa.append('spelling')
                if grammar_penalty > 10:
                    wa.append('grammar')
                if relevance < 50:
                    wa.append('relevance')
                weak_areas = ', '.join(wa) if wa else 'none'

            if not ai_suggestions:
                # Create a short actionable suggestion
                if spelling_errors:
                    ai_suggestions = f"Check spelling for {spelling_errors} word(s) and proofread your sentences."
                elif relevance < 50:
                    ai_suggestions = "Make sure to directly address the question and reference the reading where relevant."
                else:
                    ai_suggestions = "Try improving sentence clarity and punctuation for better grammar scores."

            return {
                'relevance_score': int(relevance),
                'sentence_score': int(sentence_score),
                'grammar_score': int(grammar_score),
                'overall_score': int(overall),
                'corrections': spelling_corrections,
                'spelling_errors': spelling_errors,
                'suggestions': ai_suggestions or 'Provisional fast-pass scores shown. Full suggestions will appear shortly.',
                'short_feedback': short_feedback,
                'weak_areas': weak_areas,
                'corrected_answer': sa,
                '_fast': True
            }
        except Exception:
            return self._fallback(student_answer)


# Helper to get a cached assessor
_assessor = None

def get_comprehension_assessor():
    global _assessor
    if not _assessor:
        _assessor = ComprehensionAssessor()
    return _assessor
