from dependencies.hub import *
# from celery_worker import process_document_celery
# from celery.result import AsyncResult

@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 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 errorResponse(
            f"Topic is required"
        )
    if len(topic) > 100:
        return errorResponse(
            f"Topic is too long. Maximum length is 100 characters."
        )
    if len(topic) < 5:
        return errorResponse(
            f"Topic is too short. Minimum length is 5 characters."
        )
    if not topic.isascii():
        return errorResponse(
            f"Topic contains non-ASCII characters."
        )
    result = runner.run(topic)

    # task = process_article_celery.delay(topic, course_id)
    
    article_title = truncate_filename(
            topic.replace(" ", "_").replace("/", "_")
        )
    file_path = result
    article_dir = os.path.join(working_dir, article_title)
    wiki_content_dir = os.path.join(article_dir, content_dir)
    os.makedirs(wiki_content_dir, exist_ok=True)
    imscc_file_name = os.path.join(file_path, f"{article_title}.imscc")
    # polished_article_path = os.path.join(file_path, "storm_gen_article_polished.txt")
    # article_content = read_txt_file(polished_article_path)
    
    # Generate IMSCC file after article creation
    imscc_file_path = generate_imscc(topic, output_path=imscc_file_name)

    download_link = f"{APP_URL}/download-imscc?topic={topic}"
    
    new_course = Courses(
            course_id=course_id, 
            topic=topic, 
            email = email,
            status = 'Ready',
            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 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 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 errorResponse(
            f"Topic is required"
        )
    if len(topic) > 100:
        return errorResponse(
            f"Topic is too long. Maximum length is 100 characters."
        )
    if len(topic) < 5:
        return errorResponse(
            f"Topic is too short. Minimum length is 5 characters."
        )
    if not topic.isascii():
        return errorResponse(
            f"Topic contains non-ASCII characters."
        )

    if weblinks:
        try:
            weblinks = [url.strip() for url in weblinks.split(",") if url.strip()]
            weblinks = json.dumps(weblinks)
        except json.JSONDecodeError:
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content={
                    "status": status.HTTP_400_BAD_REQUEST,
                    "message": "Weblinks: Invalid JSON format."
                }
            )
    # 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}"
    download_link = ''
    
    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 = weblinks if weblinks else None,
            status = 'Pending',
            course_step = 1,
            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 send_email(email,subject,mail_content)
    if weblinks:
        # Convert weblinks from JSON string to list
        try:
            weblink_list = json.loads(weblinks)
            weblink_csv = ", ".join(weblink_list)
        except json.JSONDecodeError:
            weblink_csv = weblinks
    else:
        weblink_csv = None
    course_data = schemas.CoursesResponse.from_orm(new_course).dict()
    course_data["weblinks"] = weblink_csv

    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("/update-course/{course_id}", response_model=schemas.CoursesResponse)
async def updateCourse(
    course_id: str,
    topic: Optional[str] = Form(None, min_length=5, max_length=100),
    email: Optional[str] = Form(None), 
    skill_development: Optional[str] = Form(None),
    education_degree: Optional[str] = Form(None),
    allocated_time: Optional[str] = Form(None),
    mode_of_delivery: Optional[str] = Form(None),
    tone: Optional[str] = Form(None),
    content_description: Optional[str] = Form(None),
    content_outcomes: Optional[str] = Form(None),
    content_objectives: Optional[str] = Form(None),
    content_outline: Optional[str] = Form(None),
    learning_approach: Optional[str] = Form(None),
    no_of_modules: Optional[int] = Form(None),
    weblinks: Optional[str] = Form(None),
    course_step: Optional[int] = Form(None),
    db: Session = Depends(db.get_db)
):
    """
    Update course details by course_id
    """
    
    course = db.query(Courses).filter(Courses.course_id == course_id).first()
    if not course:
        return errorResponse(
            f"Course not found"
        )
    
    if topic:
        if len(topic) > 100:
            return errorResponse(
                f"Topic is too long. Maximum length is 100 characters."
            )
        if len(topic) < 5:
            return errorResponse(
                f"Topic is too short. Minimum length is 5 characters."
            )
        if not topic.isascii():
            return errorResponse(
                f"Topic contains non-ASCII characters."
            )
        course.topic = topic

    if email:
        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."
                }
            )
        course.email = email

    if skill_development:
        course.skill_development = skill_development
    if education_degree:
        course.education_degree = education_degree
    if allocated_time:
        course.allocated_time = allocated_time
    if mode_of_delivery:
        course.mode_of_delivery = mode_of_delivery
    if tone:
        course.tone = tone
    if content_description:
        course.content_description = content_description
    if content_outcomes:
        course.content_outcomes = content_outcomes
    if content_objectives:
        course.content_objectives = content_objectives
    if content_outline:
        course.content_outline = content_outline
    if learning_approach:
        course.learning_approach = learning_approach
    if no_of_modules is not None:
        course.no_of_modules = no_of_modules
    if weblinks:
        try:
            weblinks = [url.strip() for url in weblinks.split(",") if url.strip()]
            course.weblinks = json.dumps(weblinks)
        except json.JSONDecodeError:
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content={
                    "status": status.HTTP_400_BAD_REQUEST,
                    "message": "Weblinks: Invalid JSON format."
                }
            )
    if course_step is not None:
        if course_step < 1:
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content={
                    "status": status.HTTP_400_BAD_REQUEST,
                    "message": "Course step must be at least 1."
                }
            )
        course.course_step = course_step

    db.commit()
    db.refresh(course)
    
    if course.weblinks:
        # Convert weblinks from JSON string to list
        try:
            weblink_list = json.loads(course.weblinks)
            weblink_csv = ", ".join(weblink_list)
        except json.JSONDecodeError:
            weblink_csv = course.weblinks
    else:
        weblink_csv = None

    response_data = schemas.CoursesResponse.from_orm(course).dict()
    response_data["weblinks"] = weblink_csv
    
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content=jsonable_encoder({
            "status": status.HTTP_200_OK,
            "message": 'Course updated successfully',
            "data": response_data
        }),
    )


@app.get("/download-imscc")
def download_imscc(topic: str):
    """
    Generate a STORM article and return it as a downloadable IMSCC file.
    """
    article_title = truncate_filename(
            topic.replace(" ", "_").replace("/", "_")
        )
    # 1. Generate the IMSCC package
    file_path = os.path.join(OUTPUT_FOLDER, article_title)
    filename = f"{article_title}.imscc"
    imscc_filepath = os.path.join(OUTPUT_FOLDER, filename)
    polished_article_path = os.path.join(file_path, "storm_gen_article_polished.txt")
    article_dir = os.path.join(working_dir, article_title)
    article_dir = os.path.join(article_dir, content_dir)

    # Create HTML content file 
    save_sections_as_html(polished_article_path, article_dir)
    # Check if the polished article file exists
    if not os.path.exists(polished_article_path):
        raise HTTPException(status_code=404, detail="Article not found. Please generate the article first.")
    
    prepare_article_question(polished_article_path, topic, article_dir)

    imscc_path = generate_imscc(topic, output_path=imscc_filepath)

    # 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.
    """
    # article = runner.run(topic)
    article_title = truncate_filename(
            topic.replace(" ", "_").replace("/", "_")
        )
    # 1. Generate the IMSCC package
    file_path = os.path.join(OUTPUT_FOLDER, article_title)
    polished_article_path = os.path.join(file_path, "storm_gen_article_polished.txt")
    article_content = read_txt_file(polished_article_path)
    questions = generate_questions_from_article(article_content)
    return {"topic": topic, "questions": questions}

@app.get("/course-list", response_model=schemas.CoursesResponse)
def get_course_list(
    email: str,
    limit: int = 10,
    offset: int = 0,
    db: Session = Depends(db.get_db)
):
    """
    Get list of all courses with pagination (limit & offset)
    """
    courses_query = db.query(Courses).filter(Courses.email == email)
    total = courses_query.count()
    courses = courses_query.offset(offset).limit(limit).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,
            "total": total,
            "limit": limit,
            "offset": offset
        },
    )
@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",
            []
        )
    article_title = truncate_filename(
            course.topic.replace(" ", "_").replace("/", "_")
        )
    # 1. Generate the IMSCC package
    file_path = os.path.join(OUTPUT_FOLDER, article_title)
    polished_article_path = os.path.join(file_path, "storm_gen_article_polished.txt")
    article_content = read_txt_file(polished_article_path)
    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"
        )
    article_title = truncate_filename(
            course.topic.replace(" ", "_").replace("/", "_")
        )
    # 1. Generate the IMSCC package
    file_path = os.path.join(OUTPUT_FOLDER, article_title)
    polished_article_path = os.path.join(file_path, "url_to_info.json")
    article_content = read_json_file(polished_article_path)
    
    citation_dict = 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}
    )

def generate_imscc(article_title: str, output_path: str = "output.imscc"):
        """
        Generate an IMSCC package from the article content.
        """
        # Create temp working directory
        sanitized_string = truncate_filename(
            article_title.replace(" ", "_").replace("/", "_")
        )
        wiki_content_dir = os.path.join(working_dir, sanitized_string)
        # content_dir= os.path.join(working_dir, content_dir)
        os.makedirs(working_dir, exist_ok=True)
        os.makedirs(wiki_content_dir, exist_ok=True)
        
        # Create the manifest
        create_manifest(
            course_title=article_title,
            content_dir=os.path.join(wiki_content_dir, content_dir),
            wiki_dir=content_dir,
            output_file=os.path.join(wiki_content_dir, "imsmanifest.xml")
        )

        with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
            for root, _, files in os.walk(wiki_content_dir):
                for file in files:
                    full_path = os.path.join(root, file)
                    arcname = os.path.relpath(full_path, start=wiki_content_dir)
                    zf.write(full_path, arcname)

        # Clean up (optional)
        # shutil.rmtree(working_dir)

        return output_path


def split_markdown_sections(md_text: str):
    """Splits the markdown into sections based on top-level (#) headings."""
    sections = re.split(r'(?=^# .+)', md_text, flags=re.MULTILINE)
    return [s.strip() for s in sections if s.strip()]

def markdown_to_html(title: str, content: str) -> str:
    body_html = markdown.markdown(content, extensions=['extra', 'codehilite', 'tables', 'toc'])
    return f"""<html>
    <head>
        <meta charset="utf-8">
        <title>{title}</title>
    </head>
    <body>
        {body_html}
    </body>
    </html>
    """

def save_sections_as_html(md_path: str, output_dir: str = "wiki_content"):
    """
    Reads a markdown file, splits it into sections, and saves each section as an HTML file.
    """
    os.makedirs(output_dir, exist_ok=True)

    # with open(md_path, "r", encoding="utf-8") as f:
    #     content = f.read()
    content = read_txt_file(md_path)
    
    sections = split_markdown_sections(content)

    for section in sections:
        lines = section.splitlines()
        title_line = lines[0].strip("# ").strip()
        filename = f"{title_line.lower().replace(' ', '_')}.html"

        html = markdown_to_html(title_line, section)
        output_path = os.path.join(output_dir, filename)
        # if os.path.exists(output_path):
        with open(output_path, "w", encoding="utf-8") as out:
            out.write(html)

    print(f"✅ Exported {len(sections)} sections to: {output_dir}")


def generate_questions_from_article(article_text: str, num_questions: int = 10):
    prompt = f"""
    Given the following article, generate {num_questions} quiz questions including:
    - Descriptive
    - Multiple choice (with 4 options)
    - Single choice
    - Fill in the blank
    - Optional (True/False)

    Format each question like:
    Type: <type>
    Question: <text>
    Options: <A, B, C, D> (only if applicable)
    Answer: <correct or expected answer>

    Article:
    \"\"\"
    {article_text}
    \"\"\"
    """
    openai_client=OpenAI()
    response = openai_client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7
    )
    
    return response.choices[0].message.content

def prepare_article_question(polished_article_path: str, topic: str,article_dir: str):
    """
    Read the polished article content and generate questions from it.
    """
    article_content = read_txt_file(polished_article_path)
    # Generate questions from the article content
    questions = generate_questions_from_article(article_content)

    article_title = truncate_filename(
            topic.replace(" ", "_").replace("/", "_")
        )

    question_filename = f"questions_{article_title.lower().replace(' ', '_')}.html"

    html = markdown_to_html(article_title, questions)
    output_path = os.path.join(article_dir, question_filename)

    with open(output_path, "w", encoding="utf-8") as out:
        out.write(html)