from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Dict, Optional

from .models.gemini import Gemini
from .utils.adf_validator import extract_zip_safe, find_adf_json_files, find_adf_text_files, AdfValidationError
from .utils.adf_processor import extract_adf_metadata, generate_adf_metadata_report
from .utils.adf_evaluator import evaluate_adf_submission


# Comprehensive AI-driven grading rubric (fair and accurate)
DEFAULT_GRADING_RUBRIC = """You are an expert Azure Data Factory instructor evaluating a student's ADF submission.

EVALUATION CRITERIA (Score 0-100):

1. COMPLETENESS & STRUCTURE (25 points)
   - Does submission include all required ADF components?
   - Pipelines, datasets, linked services properly defined?
   - All connections and references properly configured?
   - Addresses all assignment requirements?

2. ARCHITECTURE & DESIGN QUALITY (25 points)
   - Logical organization and separation of concerns?
   - Appropriate activity types for use cases?
   - Good naming conventions and clarity?
   - Reusable and maintainable design?

3. ERROR HANDLING & RESILIENCE (20 points)
   - Error handling activities or retry logic?
   - Timeout and exception handling configured?
   - Logging or monitoring mechanisms?
   - Data validation checks?

4. PARAMETERIZATION & FLEXIBILITY (15 points)
   - Uses parameters instead of hardcoded values?
   - Pipeline supports runtime configuration?
   - Reusability across environments?
   - Dynamic expressions where appropriate?

5. BEST PRACTICES & SECURITY (15 points)
   - Secure credential handling?
   - Proper data types and schemas?
   - Clean JSON/ARM template structure?
   - Performance optimization?

Provide detailed feedback with:
- What was implemented correctly
- What's missing or could improve
- Specific recommendations
- Security/performance concerns

Return score 0-100 and concise feedback (50-80 words)."""


class ADFMentor:
    def __init__(self, api_key: str, model_name: str = "gemini-2.0-flash", model: Optional[Gemini] = None):
        if model is None:
            model = Gemini(api_key=api_key, model_name=model_name)
        self.model = model

    def evaluate_adf(self, submission_path: str, prompt: Optional[str] = None, question: Optional[str] = None, use_rule_based: bool = False) -> Dict[str, Any]:
        """Evaluate an Azure Data Factory (ADF) submission.

        Accepts .json, .zip, .txt, or folder containing ADF definitions. Returns a score
        based on comprehensive AI-driven quality evaluation (default) or rule-based checks.

        Evaluation modes:
        - AI-driven (default): Comprehensive quality analysis of architecture, design, best practices
        - Rule-based (optional): Fast component counting (30+30+20+20 rubric)

        Args:
            submission_path: Path to ADF submission (.json, .zip, .txt, or folder)
            prompt: Optional custom evaluation rubric (overrides default comprehensive rubric)
            question: Optional assignment description for context
            use_rule_based: If True, use fast rule-based scoring instead of AI

        Returns:
            Dictionary with 'score' (0-100) and 'feedback' (string)
        """
        try:
            submission_path_obj = Path(submission_path).resolve()

            # Validate file/folder exists
            if not submission_path_obj.exists():
                return {
                    "score": 0,
                    "feedback": (
                        "Unable to evaluate submission.\n\n"
                        "The submission path was not found. "
                        "Please ensure you have uploaded a .json, .zip file, or folder and try again."
                    ),
                }

            # Handle folders (direct ADF project structure)
            if submission_path_obj.is_dir():
                try:
                    adf_json_objects = find_adf_json_files(str(submission_path_obj))
                    adf_text_objects = find_adf_text_files(str(submission_path_obj))
                except AdfValidationError as e:
                    return {
                        "score": 0,
                        "feedback": f"Invalid ADF folder submission.\n\n{str(e)}\n\nPlease ensure your folder contains valid ADF JSON files and try again.",
                    }
                except Exception as e:
                    return {
                        "score": 0,
                        "feedback": (
                            "Error processing ADF folder.\n\n"
                            f"Details: {str(e)}\n\n"
                            "Please ensure your folder contains valid ADF artifacts."
                        ),
                    }

            # Handle .zip files
            elif submission_path_obj.suffix.lower() == ".zip":
                try:
                    extraction_dir = extract_zip_safe(str(submission_path_obj))
                    adf_json_objects = find_adf_json_files(extraction_dir)
                    adf_text_objects = find_adf_text_files(extraction_dir)
                except AdfValidationError as e:
                    return {
                        "score": 0,
                        "feedback": f"Invalid ADF ZIP submission.\n\n{str(e)}\n\nPlease ensure your ZIP contains valid ADF artifacts and try again.",
                    }
                except Exception as e:
                    return {
                        "score": 0,
                        "feedback": (
                            "Error processing ZIP file.\n\n"
                            f"Details: {str(e)}\n\n"
                            "Please ensure your submission is a valid ZIP file."
                        ),
                    }

            # Handle .json files
            elif submission_path_obj.suffix.lower() == ".json":
                try:
                    json_content = submission_path_obj.read_text(encoding="utf-8-sig")
                    json_obj = json.loads(json_content)
                    adf_json_objects = [{
                        "filename": submission_path_obj.name,
                        "path": str(submission_path_obj),
                        "content": json_obj,
                        "type": "json_upload"
                    }]
                    adf_text_objects = []
                except json.JSONDecodeError as e:
                    return {
                        "score": 0,
                        "feedback": (
                            "Invalid JSON file.\n\n"
                            f"Error: {str(e)}\n\n"
                            "Please ensure your JSON file is properly formatted."
                        ),
                    }
                except Exception as e:
                    return {
                        "score": 0,
                        "feedback": (
                            "Error reading JSON file.\n\n"
                            f"Details: {str(e)}"
                        ),
                    }

            # Handle .txt files (AI-driven written evaluation)
            elif submission_path_obj.suffix.lower() == ".txt":
                try:
                    text_answer = submission_path_obj.read_text(encoding="utf-8").strip()
                except Exception as e:
                    return {
                        "score": 0,
                        "feedback": (
                            "Error reading text file.\n\n"
                            f"Details: {str(e)}"
                        ),
                    }

                if not prompt:
                    return {
                        "score": 0,
                        "feedback": (
                            "Text-only submissions require an AI grading prompt.\n\n"
                            "Please call evaluate_adf(..., prompt=..., question=...) for .txt files."
                        ),
                    }

                result = self.model.evaluate(
                    question=question or "Evaluate this written ADF explanation.",
                    answer=text_answer,
                    prompt=prompt,
                )
                result["metadata_report"] = "Text-only submission (no JSON metadata extracted)."
                return result

            # Reject unsupported file types
            else:
                return {
                    "score": 0,
                    "feedback": (
                        "Unsupported file format.\n\n"
                        f"Received: {submission_path_obj.suffix}\n\n"
                        "Please upload an ADF submission as a .json, .zip, folder, or .txt file."
                    ),
                }

            # Extract metadata
            try:
                metadata = extract_adf_metadata(adf_json_objects)
            except Exception as e:
                return {
                    "score": 0,
                    "feedback": (
                        "Error extracting ADF metadata.\n\n"
                        f"Details: {str(e)}\n\n"
                        "Please ensure your submission contains valid ADF artifacts."
                    ),
                }

            # Evaluate based on mode
            if use_rule_based:
                # Fast rule-based evaluation (for backward compatibility)
                result = evaluate_adf_submission(metadata)
                
                # Attach metadata report for transparency
                metadata_report = generate_adf_metadata_report(metadata)
                result["metadata_report"] = metadata_report
            else:
                # AI-driven evaluation (default - comprehensive and fair)
                metadata_report = generate_adf_metadata_report(metadata)
                
                # Prepare full context for AI
                json_summary = json.dumps({
                    "metadata": metadata,
                    "file_count": len(adf_json_objects),
                    "files": [obj.get("filename") for obj in adf_json_objects]
                }, indent=2)
                
                answer_context = f"ADF Submission Analysis:\n\n{metadata_report}\n\nJSON Structure:\n{json_summary}"
                if adf_text_objects:
                    joined_text = "\n\n".join(
                        f"[{obj['filename']}]\n{obj['content']}"
                        for obj in adf_text_objects if obj.get("content")
                    )
                    if joined_text:
                        answer_context += f"\n\nCompanion Text Notes:\n{joined_text}"
                
                # Use custom prompt if provided, otherwise use comprehensive default rubric
                grading_rubric = prompt if prompt else DEFAULT_GRADING_RUBRIC
                
                result = self.model.evaluate(
                    question=question or "Evaluate this Azure Data Factory submission for quality, accuracy, and completeness.",
                    answer=answer_context,
                    prompt=grading_rubric
                )
                
                # Add metadata report for transparency
                result["metadata_report"] = metadata_report

            return result

        except Exception as e:
            return {
                "score": 0,
                "feedback": (
                    "Unexpected error evaluating ADF submission.\n\n"
                    f"Details: {str(e)}\n\n"
                    "Please contact support if this persists."
                ),
            }