import os
from fastapi import Depends, FastAPI,Form, Request, HTTPException
import re
from datetime import datetime
from pydantic import BaseModel
from typing import Optional
from dotenv import load_dotenv
from fastapi import HTTPException, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse, FileResponse

from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
import db_config.database as db
from db_config.models import Courses
import db_config.schemas as schemas
import json
import zipfile
import uuid
import markdown
import dependencies.helper as helper
from dependencies.StormRunner import StormRunner
from celery_worker import process_article_celery
from celery.result import AsyncResult

APP_URL = os.getenv("APP_URL")
EMAIL_REGEX = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
app = FastAPI(title="Sandstorm: AI Assisted Content Generator")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Replace with your frontend URL
    allow_credentials=True,
    allow_methods=["*"],  # Allows all methods like GET, POST, PUT, etc.
    allow_headers=["*"],  # Allows all headers
)
@app.get("/generate-article", response_model=schemas.CoursesResponse)
async def generate(topic: str, email: str, db: Session = Depends(db.get_db)):
    """
    Generate a STORM article and return it as a downloadable IMSCC file.
    """
    course_id =  f"{os.getenv('COURSE_PREFIX')}-{uuid.uuid4()}"

    if not email:
        return helper.errorResponse(
            f"Email is required"
        )

    if not re.match(EMAIL_REGEX, email):
        return JSONResponse(
            status_code=status.HTTP_400_BAD_REQUEST,
            content={
                "status": status.HTTP_400_BAD_REQUEST,
                "message": "Email: Invalid value."
            }
        )
    if not topic:
        return helper.errorResponse(
            f"Topic is required"
        )
    if len(topic) > 100:
        return helper.errorResponse(
            f"Topic is too long. Maximum length is 100 characters."
        )
    if len(topic) < 5:
        return helper.errorResponse(
            f"Topic is too short. Minimum length is 5 characters."
        )
    if not topic.isascii():
        return helper.errorResponse(
            f"Topic contains non-ASCII characters."
        )
    # runner = StormRunner(topic)
    # result = runner.process_article()

    task = process_article_celery.delay(topic, course_id)
    print(f"Task ID: {task.id}")
    download_link = f"{APP_URL}/download-imscc?topic={topic}"
    
    new_course = Courses(
            course_id=course_id, 
            topic=topic, 
            task_id=task.id,
            email = email,
            status = 'Processing',
            course_imscc_url = download_link,
            created_at = datetime.utcnow(),
            updated_at = datetime.utcnow()
        )
    db.add(new_course)
    db.commit()
    db.refresh(new_course)
    
    subject = f"Your topic {topic} is ready to download"
    
    mail_content = f"Dear User,<br/><p>Please find the below link to download imscc file.</p><p>Download Link: <a href='{download_link}' target='_blank'>{download_link}</a></p><p></p><br/>Warm Regards,<br/>Answerous Team"

    await helper.send_email(email,subject,mail_content)

    course_data = schemas.CoursesResponse.from_orm(new_course).dict()
    encoded_data = jsonable_encoder(course_data)

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Data retrieved successfully',
            "data": encoded_data
        },
    )

@app.post("/create-a-course", response_model=schemas.CoursesResponse)
async def createCourse(
    topic: str = Form(..., min_length=5, max_length=100),
    email: str = Form(...), 
    skill_development: str = Form(...),
    education_degree: str = Form(...),
    allocated_time:str = Form(...),
    language: str = Form(...),
    mode_of_delivery: str = Form(...),
    tone: str = Form(...),
    weblinks: Optional[str] = Form(None),
    db: Session = Depends(db.get_db)
):
    """
    Generate a STORM article and return it as a downloadable IMSCC file.
    """
    course_id =  f"{os.getenv('COURSE_PREFIX')}-{uuid.uuid4()}"

    if not email:
        return helper.errorResponse(
            f"Email is required"
        )

    if not re.match(EMAIL_REGEX, email):
        return JSONResponse(
            status_code=status.HTTP_400_BAD_REQUEST,
            content={
                "status": status.HTTP_400_BAD_REQUEST,
                "message": "Email: Invalid value."
            }
        )
    if not topic:
        return helper.errorResponse(
            f"Topic is required"
        )
    if len(topic) > 100:
        return helper.errorResponse(
            f"Topic is too long. Maximum length is 100 characters."
        )
    if len(topic) < 5:
        return helper.errorResponse(
            f"Topic is too short. Minimum length is 5 characters."
        )
    if not topic.isascii():
        return helper.errorResponse(
            f"Topic contains non-ASCII characters."
        )
    # runner = StormRunner(topic)
    # result = runner.process_article()

    task = process_article_celery.delay(topic, course_id)
    print(f"Task ID: {task.id}")
    download_link = f"{APP_URL}/download-imscc?topic={topic}"
    
    new_course = Courses(
            course_id=course_id, 
            topic=topic, 
            task_id=task.id,
            email = email,
            skill_development = skill_development,
            education_degree = education_degree,
            allocated_time = allocated_time,
            language = language,
            mode_of_delivery = mode_of_delivery,
            tone = tone,
            weblinks = json.loads(weblinks) if weblinks else None,
            status = 'Processing',
            course_imscc_url = download_link,
            created_at = datetime.utcnow(),
            updated_at = datetime.utcnow()
        )
    db.add(new_course)
    db.commit()
    db.refresh(new_course)
    
    subject = f"Your topic {topic} is ready to download"
    
    mail_content = f"Dear User,<br/><p>Please find the below link to download imscc file.</p><p>Download Link: <a href='{download_link}' target='_blank'>{download_link}</a></p><p></p><br/>Warm Regards,<br/>Answerous Team"

    # await helper.send_email(email,subject,mail_content)

    course_data = schemas.CoursesResponse.from_orm(new_course).dict()
    encoded_data = jsonable_encoder(course_data)

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Data retrieved successfully',
            "data": encoded_data
        },
    )

@app.get("/download-imscc")
def download_imscc(topic: str):
    """
    Generate a STORM article and return it as a downloadable IMSCC file.
    """
    runner = StormRunner(topic)
    imscc_path = runner.generate_imscc()
    filename = f"{runner.article_title}.imscc"

    # 2. Return the IMSCC file as a downloadable response
    return FileResponse(
        imscc_path,
        media_type="application/zip",
        filename=filename
    )

@app.get("/generate-quiz")
def generate_quiz(topic: str):
    """
    Generate quiz questions based on the article content.
    """
    if not topic:
        return {"error": "Topic is required"}
    if len(topic) > 100:
        return {"error": "Topic is too long. Maximum length is 100 characters."}
    if len(topic) < 5:
        return {"error": "Topic is too short. Minimum length is 5 characters."}
    if not topic.isascii():
        return {"error": "Topic contains non-ASCII characters."}
    questions = []
    try:
        # Initialize the StormRunner with the topic
        runner = StormRunner(topic)
        # Generate questions from the article
        questions = runner.generate_questions_from_article()
    except Exception as e:
        return {"error": str(e)}
    if not questions:
        return {"error": "No questions generated for the given topic."}
    
    # runner = StormRunner(topic)
    # runner.generate_questions_from_article()
    return {"topic": topic, "questions": questions}

@app.get("/course-list", response_model=schemas.CoursesResponse)
def get_course_list(email: str, db: Session = Depends(db.get_db)):
    """
    Get list of all courses
    """
    courses = db.query(Courses).filter(Courses.email == email).all()
    if not courses:
        return successResponse(
            f"No courses found",
            []
        )

    # Convert ORM to schema and encode properly
    course_data = [schemas.CoursesResponse.from_orm(course) for course in courses]
    encoded_data = jsonable_encoder(course_data)
    
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Data retrieved successfully',
            "data": encoded_data
        },
    )
@app.get("/course/{course_id}", response_model=schemas.CoursesResponse)
def get_course(course_id: str, db: Session = Depends(db.get_db)):
    """
    Get course details by course_id
    """
    course = db.query(Courses).filter(Courses.course_id == course_id).first()
    if not course:
        return successResponse(
            f"Course not found",
            []
        )
   
    runner = StormRunner(course.topic)
    article_content = runner.get_article_content()
    if not article_content:
        return errorResponse(
            f"Article content not found for course {course_id}"
        )
    response_data = {
        **jsonable_encoder(schemas.CoursesResponse.from_orm(course).dict()),
        "course_content": article_content
    }

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Data retrieved successfully',
            "data": response_data
        },
    )
@app.delete("/course/delete/{course_id}", response_model=schemas.CoursesResponse)
def delete_course(course_id: str, db: Session = Depends(db.get_db)):
    """
    Delete course by course_id
    """
    course = db.query(Courses).filter(Courses.course_id == course_id).first()
    if not course:
        return errorResponse(
            f"Course not found"
        )
    db.delete(course)
    db.commit()
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Course deleted successfully',
            "data": schemas.CoursesResponse.from_orm(course).dict()
        },
    )
@app.get("/course/{course_id}/citation/{index}", response_model=schemas.CoursesResponse)
def get_course_citation(course_id: str, index: int, db: Session = Depends(db.get_db)):
    """
    Get course citation by course_id
    """
    course = db.query(Courses).filter(Courses.course_id == course_id).first()
    if not course:
        return errorResponse(
            f"Course not found"
        )
    
    runner = StormRunner(course.topic)
    article_content = runner.get_url_to_info()

    citation_dict = helper.construct_citation_dict_from_article(article_content, index)
    

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "status": status.HTTP_200_OK,
            "message": 'Data retrieved successfully',
            "data": citation_dict
        },
    )
# Custom validation error handler
@app.exception_handler(RequestValidationError)
async def custom_validation_exception_handler(request: Request, exc: RequestValidationError):
    errors = [
        {
            "field": ".".join(str(loc) for loc in error["loc"]),
            "message": error["msg"],
            "type": error["type"],
        }
        for error in exc.errors()
    ]
    
    return JSONResponse(
        status_code=400,
        content={
            "status": 400,
            "message": errors[0]['message'],
            "field": errors[0]['field']
        },
    )

@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request, exc: HTTPException):
    if exc.status_code == 403 and exc.detail == "Not authenticated":
        return JSONResponse(
            status_code=401,
            content={"status": 401, "message": "Missing API Token"},
        )
    return JSONResponse(
        status_code=exc.status_code,
        content={"status": exc.status_code, "message": exc.detail}
    )

