from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel
from typing import List, Dict, Optional, Any
import json
import logging
from dataclasses import asdict
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
# Import your existing modules
from tna_dep import TNASystem
from tna_skill_second import SkillAnalysisSystem
# Import the new domain-aware course filter instead of the old one
from tester import DomainAwareCourseFilter
from typing import Union
from online_course import OnlineCourseRecommendationSystem

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="TNA System API", version="1.0.0")

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    logger.error(f"Validation error: {exc}")
    return JSONResponse(
        status_code=422,
        content={"detail": exc.errors(), "body": exc.body}
    )


@app.middleware("http")
async def log_requests(request: Request, call_next):
    if request.url.path == "/analyze-skills-courses":
        body = await request.body()
        logger.info(f"Raw request body: {body.decode()}")
        
        # Re-create request for further processing
        async def receive():
            return {"type": "http.request", "body": body}
        
        request._receive = receive
    
    response = await call_next(request)
    return response

# =================== DEPARTMENT ENDPOINT MODELS ===================
class Department(BaseModel):
    id: int
    name: str

class DepartmentRequest(BaseModel):
    user_prompt: str
    organization_departments: List[Department]

class DepartmentResponse(BaseModel):
    identified_department: str
    department_id: int

# =================== SKILLS & COURSES ENDPOINT MODELS ===================
class FilteredSkill(BaseModel):
    skill_id: int
    skill_name: str
    skill_level: Optional[str] = None
    skill_count: Optional[int] = None

class AllSkillData(BaseModel):
    skill_id: int
    skill_name: str

class OfflineCourse(BaseModel):
    course_id: str
    course_name: str
    short_description: str
    description: str
    skills: List[Union[str, Dict[str, Any]]] 

class SkillsCourseRequest(BaseModel):
    user_query: str
    admin_name: str
    department_name: str
    filtered_skills: List[FilteredSkill]
    total_users_in_department: int
    all_skills_data: List[AllSkillData]
    offline_courses: List[OfflineCourse]

class CourseRecommendation(BaseModel):
    course_id: str
    name: str
    reason: str
    percentage: float

class OnlineCourseRecommendation(BaseModel):
    title: str
    platform: str
    instructor: str
    duration: str
    skill_level: str
    description: str
    url: str
    price: str
    rating: str
    skills_covered: List[str]
    relevance_score: int
    reason: str

class SkillsCourseResponse(BaseModel):
    skill_analysis: Dict[str, Any]
    courses_offline: List[CourseRecommendation]
    online_recommended_courses: List[OnlineCourseRecommendation]
    strategic_presentation: str

class OnlineCourseRequest(BaseModel):
    domain: str
    field_prompt: str
    max_courses: Optional[int] = 10

class OnlineCourseResponse(BaseModel):
    success: bool
    domain: str
    field_prompt: str
    total_recommendations: int
    course_recommendations: List[OnlineCourseRecommendation]
    search_summary: str

# =================== SYSTEM INITIALIZATION ===================
class TNAIntegratedSystem:
    """Integrated TNA System combining department, skill, and course analysis"""
    
    def __init__(self):
        """Initialize all three systems"""
        self.department_system = TNASystem()
        self.skill_system = SkillAnalysisSystem()
        # Use the new domain-aware course filter
        self.course_system = DomainAwareCourseFilter(model_name="gemma3:12b", batch_size=30)
         # *** NEW SYSTEM *** - Add online course recommendation system
        self.online_course_system = OnlineCourseRecommendationSystem(model_name="gemma3:12b")
        logger.info("TNA Integrated System initialized with Domain-Aware Course Filter and Online Course Recommendations")
    
    def process_skills_and_courses(self, request: SkillsCourseRequest) -> SkillsCourseResponse:
        """
        Process skill analysis and course recommendations with strategic presentation
        
        Args:
            request: SkillsCourseRequest with all necessary data
            
        Returns:
            SkillsCourseResponse with skill analysis, course recommendations, and strategic presentation
        """
        try:
            # Step 1: Skill Analysis
            skill_request = {
                "user_query": request.user_query,
                "admin_name": request.admin_name,
                "department_name": request.department_name,
                "filtered_skills": [
                    {
                        "skill_id": skill.skill_id,
                        "skill_name": skill.skill_name,
                        "skill_level": skill.skill_level,
                        "skill_count": skill.skill_count
                    }
                    for skill in request.filtered_skills
                ],
                "all_skills_data": [
                    {
                        "skill_id": skill.skill_id,
                        "skill_name": skill.skill_name
                    }
                    for skill in request.all_skills_data
                ]
            }
            
            # Get both raw analysis and strategic presentation
            both_formats = self.skill_system.process_with_both_formats(
                json.dumps(skill_request)
            )
            skill_result = both_formats['raw_analysis']
            strategic_presentation = both_formats['strategic_presentation']
            
            logger.info(f"Skill analysis completed: {skill_result.get('total_skills_recommended', 0)} skills")
            
            # Step 2: Domain-Aware Course Analysis
            courses_offline = []
            
            if skill_result.get('success', False) and skill_result.get('identified_field'):
                # Map identified_field to domain for the domain-aware filter
                domain = self._map_field_to_domain(skill_result['identified_field'])
                field_prompt = request.user_query  # Use the original user query as field prompt
                
                # Prepare course request for domain-aware filter
                course_request = {
                    "domain": domain,
                    "field_prompt": field_prompt,
                    "courses": [
                        {
                            "course_id": course.course_id,
                            "course_name": course.course_name,
                            "course_short_description": course.short_description,
                            "course_description": course.description,
                            "skill": ",".join([str(skill) if isinstance(skill, str) else skill.get('skill_name', str(skill)) for skill in course.skills]) if course.skills else ""
                        }
                        for course in request.offline_courses
                    ]
                }
                
                # Process with domain-aware course filter
                course_result = self.course_system.process_json_request(
                    json.dumps(course_request)
                )
                
                logger.info(f"Domain-aware course analysis: {course_result.get('total_matching_courses', 0)} courses matched")
                
                # Generate offline  course recommendations using the new format
                if course_result.get('success', False) and course_result.get('course_matches'):
                    courses_offline = self._convert_domain_matches_to_recommendations(
                        course_result['course_matches']
                    )
                
                 # *** NEW STEP *** - Step 3: Online Course Recommendations
                online_course_request = {
                    "domain": domain,
                    "field_prompt": field_prompt,
                    "max_courses": 8  # Limit to 8 online courses
                }

                online_course_result = self.online_course_system.process_json_request(
                    json.dumps(online_course_request)
                )
                
                logger.info(f"Online course analysis: {online_course_result.get('total_recommendations', 0)} courses recommended")
                
                # Convert online course recommendations
                if online_course_result.get('success', False) and online_course_result.get('course_recommendations'):
                    online_recommended_courses  = self._convert_online_recommendations(
                        online_course_result['course_recommendations']
                    )
            
            
            # Combine results - maintaining the same output format
            return SkillsCourseResponse(
                skill_analysis={
                    "identified_field": skill_result.get('identified_field', 'Unknown'),
                    "total_skills_recommended": skill_result.get('total_skills_recommended', 0),
                    "recommended_skills": skill_result.get('recommended_skills', []),
                    "analysis_summary": skill_result.get('analysis_summary', ''),
                    "case_applied": skill_result.get('case_applied', ''),
                    "department_coverage": skill_result.get('department_coverage', 0.0),
                    "success": skill_result.get('success', False)
                },
                courses_offline=courses_offline[:9],
                online_recommended_courses=online_recommended_courses,
                strategic_presentation=strategic_presentation
            )
        
        except Exception as e:
            logger.error(f"Error in skills and courses processing: {e}")
            raise HTTPException(status_code=500, detail=f"Skills and courses processing failed: {str(e)}")

    def _convert_online_recommendations(self, online_recommendations: List[Dict]) -> List[OnlineCourseRecommendation]:
        """Convert online course recommendations to the expected format"""
        recommendations = []
        
        for rec in online_recommendations:
            recommendation = OnlineCourseRecommendation(
                title=rec['course_name'],
                platform=rec['platform'],
                instructor=rec['instructor'],
                duration=rec['duration'],
                skill_level=rec['skill_level'],
                description=rec['description'],
                url=rec['url'],
                price=rec['price'],
                rating=rec['rating'],
                skills_covered=rec['skills_covered'],
                relevance_score=rec['match_percentage'],
                reason=rec['recommendation_reason']
            )
            recommendations.append(recommendation)
        
        return recommendations
    
    def _map_field_to_domain(self, identified_field: str) -> str:
        """
        Map identified field to broader domain categories for domain-aware filtering
        
        Args:
            identified_field: The field identified by skill analysis
            
        Returns:
            Mapped domain category
        """
        field_lower = identified_field.lower()
        
        # Technology domains
        if any(tech_term in field_lower for tech_term in [
            'programming', 'development', 'software', 'web', 'mobile', 'frontend', 
            'backend', 'fullstack', 'python', 'javascript', 'java', 'react', 
            'angular', 'node', 'database', 'api', 'cloud', 'devops', 'ai', 
            'machine learning', 'data science', 'cybersecurity', 'technology'
        ]):
            return "Technology"
        
        # Business domains
        elif any(business_term in field_lower for business_term in [
            'sales', 'marketing', 'business', 'management', 'leadership', 
            'finance', 'accounting', 'operations', 'strategy', 'consulting',
            'project management', 'product management', 'customer service'
        ]):
            return "Business"
        
        # Healthcare domains
        elif any(health_term in field_lower for health_term in [
            'healthcare', 'medical', 'nursing', 'pharmacy', 'clinical',
            'patient care', 'health'
        ]):
            return "Healthcare"
        
        # Education domains
        elif any(edu_term in field_lower for edu_term in [
            'education', 'teaching', 'training', 'learning', 'academic'
        ]):
            return "Education"
        
        # Creative domains
        elif any(creative_term in field_lower for creative_term in [
            'design', 'creative', 'art', 'graphics', 'ui/ux', 'content',
            'writing', 'media'
        ]):
            return "Creative"
        
        # Default to Technology for technical skills or Business for others
        else:
            # If it contains any technical keywords, default to Technology
            if any(keyword in field_lower for keyword in ['technical', 'computer', 'digital', 'system']):
                return "Technology"
            else:
                return "Business"
    
    def _convert_domain_matches_to_recommendations(self, course_matches: List[Dict]) -> List[CourseRecommendation]:
        """
        Convert domain-aware course matches to the expected CourseRecommendation format
        
        Args:
            course_matches: List of matches from domain-aware filter
            
        Returns:
            List of CourseRecommendation objects maintaining the same output format
        """
        recommendations = []
        
        for match in course_matches:
            recommendation = CourseRecommendation(
                course_id=match['course_id'],
                name=match['course_name'],
                reason=match['reason'],
                percentage=float(match['match_percentage'])  # Domain-aware filter already provides percentage
            )
            recommendations.append(recommendation)
        
        # Sort by percentage (highest first) - domain-aware filter should already do this
        recommendations.sort(key=lambda x: x.percentage, reverse=True)
        
        return recommendations

# Initialize the integrated system
tna_system = TNAIntegratedSystem()

# =================== API ENDPOINTS ===================

@app.post("/analyze-department", response_model=DepartmentResponse)
async def analyze_department(request: DepartmentRequest):
    """
    Department Analysis endpoint - FIRST CALL
    
    Identifies the most relevant department based on user prompt
    """
    try:
        logger.info(f"Processing department analysis for: {request.user_prompt}")
        
        # Prepare department request
        department_request = {
            "user_prompt": request.user_prompt,
            "organization_departments": [
                {"id": dept.id, "name": dept.name} 
                for dept in request.organization_departments
            ]
        }
        
        # Process department analysis
        department_result = tna_system.department_system.process_json_request(
            json.dumps(department_request)
        )
        
        logger.info(f"Department analysis result: {department_result}")
        
        # Extract results
        if department_result.get('success', False) and department_result.get('matched', False):
            identified_department = department_result.get('department_name', 'Unknown')
            department_id = department_result.get('department_id', 0)
        else:
            # If no match found, return the inferred department with ID 0
            identified_department = department_result.get('inferred_department', 'Unknown')
            department_id = 0
        
        result = DepartmentResponse(
            identified_department=identified_department,
            department_id=department_id
        )
        
        logger.info(f"Department analysis completed: {identified_department} (ID: {department_id})")
        return result
    
    except Exception as e:
        logger.error(f"Department analysis failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/analyze-skills-courses", response_model=SkillsCourseResponse)
async def analyze_skills_courses(request: SkillsCourseRequest):
    """
    Skills and Courses Analysis endpoint - SECOND CALL
    
    Processes skill analysis and course recommendations based on department context
    Now uses Domain-Aware Course Filter for improved course matching
    """
    try:
        logger.info(f"Processing skills and courses analysis for: {request.user_query}")
        logger.info(f"Request data: {request.dict()}")
        result = tna_system.process_skills_and_courses(request)
        logger.info("Skills and courses analysis completed successfully with domain-aware filtering")
        return result
    
    except Exception as e:
        logger.error(f"Skills and courses analysis failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {"status": "healthy", "message": "TNA System API is running with Domain-Aware Course Filter"}

@app.get("/")
async def root():
    """Root endpoint with API information"""
    return {
        "message": "TNA System API - Enhanced with Domain-Aware Course Filter",
        "version": "1.0.0",
        "enhancement": "Now uses Domain-Aware Course Filter for improved course matching",
        "flow": "Two-step process: 1) Department Analysis → 2) Skills & Courses Analysis",
        "endpoints": {
            "analyze-department": "POST /analyze-department - FIRST CALL: Department identification",
            "analyze-skills-courses": "POST /analyze-skills-courses - SECOND CALL: Skills analysis and domain-aware course recommendations",
            "health": "GET /health - Health check",
            "docs": "GET /docs - API documentation"
        }
    }

# =================== EXAMPLE REQUESTS FOR TESTING ===================

example_department_request = {
    "user_prompt": "Want to improve my teams sales ability",
    "organization_departments": [
        {"id": 160, "name": "Operations"},
        {"id": 161, "name": "HR"},
        {"id": 162, "name": "Finance"},
        {"id": 170, "name": "Product Management"},
        {"id": 171, "name": "sales"},
        {"id": 172, "name": "Developer"}
    ]
}

example_skills_courses_request = {
    "user_query": "Want to improve my technical ability",
    "admin_name": "Ascent",
    "department_name": "Developer",
    "filtered_skills": [
        {
            "skill_id": 120,
            "skill_name": "Python",
            "skill_level": "Intermediate",
            "skill_count": 3
        }
    ],
    "total_users_in_department": 5,
    "all_skills_data": [
        {"skill_id": 1375, "skill_name": "communication skill"},
        {"skill_id": 1376, "skill_name": "technical skills"},
        {"skill_id": 121, "skill_name": "JavaScript"},
        {"skill_id": 122, "skill_name": "Python"},
        {"skill_id": 123, "skill_name": "Angular"}
    ],
    "offline_courses": [
        {
            "course_id": "32",
            "course_name": "General Knowledge",
            "short_description": "General Knowledge",
            "description": "",
            "skills": []
        },
        {
            "course_id": "43",
            "course_name": "Python Programming for Complete Beginners",
            "short_description": "Python Programming for Complete Beginners",
            "description": "Learn Python from scratch",
            "skills": ["python", "programming"]
        },
        {
            "course_id": "63",
            "course_name": "Angular Basic",
            "short_description": "Angular framework course",
            "description": "Complete Angular course",
            "skills": ["angular", "typescript", "javascript"]
        }
    ]
}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)