import re
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import or_

from app.database import get_db
from app.models.user import User
from app.models.question import Question, Answer, QuestionVote, AnswerVote
from app.models.tag import Tag
from app.schemas import QuestionCreate, AnswerCreate
from app.services.auth import require_user, get_current_user
from app.services.notifications import notify_followers, create_notification

router = APIRouter(prefix="/api/questions", tags=["questions"])


def get_or_create_tag(db: Session, tag_name: str) -> Tag:
    slug = re.sub(r"[^a-z0-9]+", "-", tag_name.lower()).strip("-")
    tag = db.query(Tag).filter(Tag.slug == slug).first()
    if not tag:
        tag = Tag(name=tag_name.strip(), slug=slug)
        db.add(tag)
        db.flush()
    return tag


@router.post("")
def create_question(data: QuestionCreate, user: User = Depends(require_user), db: Session = Depends(get_db)):
    q = Question(title=data.title, body=data.body, field_id=data.field_id, author_id=user.id)
    for tag_name in data.tags:
        if tag_name.strip():
            q.tags.append(get_or_create_tag(db, tag_name))
    db.add(q)
    db.commit()
    db.refresh(q)
    return {"id": q.id, "title": q.title}


@router.get("")
def list_questions(
    q: str = Query(""),
    field_id: int | None = Query(None),
    tag: str | None = Query(None),
    author: str | None = Query(None),
    sort: str = Query("newest"),
    page: int = Query(1, ge=1),
    per_page: int = Query(20, ge=1, le=100),
    db: Session = Depends(get_db),
):
    query = db.query(Question).options(
        joinedload(Question.author),
        joinedload(Question.field),
        joinedload(Question.tags),
    )
    if q:
        query = query.filter(or_(Question.title.ilike(f"%{q}%"), Question.body.ilike(f"%{q}%")))
    if field_id:
        query = query.filter(Question.field_id == field_id)
    if tag:
        query = query.join(Question.tags).filter(Tag.slug == tag)
    if author:
        query = query.join(Question.author).filter(
            or_(User.full_name.ilike(f"%{author}%"), User.username.ilike(f"%{author}%"))
        )
    if sort == "oldest":
        query = query.order_by(Question.created_at.asc())
    elif sort == "popular":
        query = query.order_by(Question.vote_count.desc())
    elif sort == "unanswered":
        query = query.filter(Question.answer_count == 0).order_by(Question.created_at.desc())
    else:
        query = query.order_by(Question.created_at.desc())

    total = query.count()
    questions = query.offset((page - 1) * per_page).limit(per_page).all()
    return {
        "total": total, "page": page, "per_page": per_page,
        "results": [
            {
                "id": qn.id, "title": qn.title, "body": qn.body[:200],
                "is_closed": qn.is_closed, "view_count": qn.view_count,
                "vote_count": qn.vote_count, "answer_count": qn.answer_count,
                "created_at": str(qn.created_at),
                "author": {"id": qn.author.id, "full_name": qn.author.full_name, "username": qn.author.username, "is_verified": qn.author.is_verified} if qn.author else None,
                "field": {"id": qn.field.id, "name": qn.field.name, "slug": qn.field.slug, "color": qn.field.color} if qn.field else None,
                "tags": [{"name": t.name, "slug": t.slug} for t in qn.tags],
            }
            for qn in questions
        ],
    }


@router.get("/{question_id}")
def get_question(question_id: int, db: Session = Depends(get_db)):
    q = db.query(Question).options(
        joinedload(Question.author),
        joinedload(Question.field),
        joinedload(Question.tags),
        joinedload(Question.answers).joinedload(Answer.author),
    ).filter(Question.id == question_id).first()
    if not q:
        raise HTTPException(status_code=404, detail="Question not found")
    q.view_count += 1
    db.commit()
    return {
        "id": q.id, "title": q.title, "body": q.body,
        "is_closed": q.is_closed, "view_count": q.view_count,
        "vote_count": q.vote_count, "answer_count": q.answer_count,
        "created_at": str(q.created_at),
        "author": {
            "id": q.author.id, "full_name": q.author.full_name, "username": q.author.username,
            "is_verified": q.author.is_verified, "avatar_url": q.author.avatar_url,
            "affiliation": {"designation": q.author.primary_affiliation.designation, "institution": q.author.primary_affiliation.institution} if q.author.primary_affiliation else None,
        },
        "field": {"id": q.field.id, "name": q.field.name, "slug": q.field.slug, "color": q.field.color} if q.field else None,
        "tags": [{"name": t.name, "slug": t.slug} for t in q.tags],
        "answers": [
            {
                "id": a.id, "body": a.body, "is_accepted": a.is_accepted,
                "vote_count": a.vote_count, "created_at": str(a.created_at),
                "author": {
                    "id": a.author.id, "full_name": a.author.full_name, "username": a.author.username,
                    "is_verified": a.author.is_verified, "avatar_url": a.author.avatar_url,
                    "affiliation": {"designation": a.author.primary_affiliation.designation, "institution": a.author.primary_affiliation.institution} if a.author.primary_affiliation else None,
                },
            }
            for a in sorted(q.answers, key=lambda x: (-x.is_accepted, -x.vote_count, x.created_at))
        ],
    }


@router.post("/{question_id}/answers")
def add_answer(question_id: int, data: AnswerCreate, user: User = Depends(require_user), db: Session = Depends(get_db)):
    q = db.query(Question).filter(Question.id == question_id).first()
    if not q:
        raise HTTPException(status_code=404, detail="Question not found")
    answer = Answer(question_id=question_id, author_id=user.id, body=data.body)
    db.add(answer)
    q.answer_count += 1
    db.commit()
    # Notify question author and followers
    if q.author_id != user.id:
        create_notification(db, q.author_id, user.id, "answer",
                            f"{user.full_name} answered your question \"{q.title}\"",
                            f"/questions/{question_id}")
    notify_followers(db, "question", question_id, user.id, "answer",
                     f"{user.full_name} answered \"{q.title}\"",
                     f"/questions/{question_id}")
    return {"id": answer.id, "message": "Answer posted"}


@router.post("/{question_id}/vote")
def vote_question(question_id: int, value: int = Query(..., ge=-1, le=1), user: User = Depends(require_user), db: Session = Depends(get_db)):
    q = db.query(Question).filter(Question.id == question_id).first()
    if not q:
        raise HTTPException(status_code=404, detail="Question not found")
    existing = db.query(QuestionVote).filter(QuestionVote.question_id == question_id, QuestionVote.user_id == user.id).first()
    if existing:
        q.vote_count -= existing.value
        if value == 0:
            db.delete(existing)
        else:
            existing.value = value
        q.vote_count += value if value != 0 else 0
    else:
        if value != 0:
            db.add(QuestionVote(question_id=question_id, user_id=user.id, value=value))
            q.vote_count += value
    db.commit()
    return {"vote_count": q.vote_count}


@router.post("/answers/{answer_id}/vote")
def vote_answer(answer_id: int, value: int = Query(..., ge=-1, le=1), user: User = Depends(require_user), db: Session = Depends(get_db)):
    a = db.query(Answer).filter(Answer.id == answer_id).first()
    if not a:
        raise HTTPException(status_code=404, detail="Answer not found")
    existing = db.query(AnswerVote).filter(AnswerVote.answer_id == answer_id, AnswerVote.user_id == user.id).first()
    if existing:
        a.vote_count -= existing.value
        if value == 0:
            db.delete(existing)
        else:
            existing.value = value
        a.vote_count += value if value != 0 else 0
    else:
        if value != 0:
            db.add(AnswerVote(answer_id=answer_id, user_id=user.id, value=value))
            a.vote_count += value
    db.commit()
    return {"vote_count": a.vote_count}


@router.post("/answers/{answer_id}/accept")
def accept_answer(answer_id: int, user: User = Depends(require_user), db: Session = Depends(get_db)):
    a = db.query(Answer).filter(Answer.id == answer_id).first()
    if not a:
        raise HTTPException(status_code=404, detail="Answer not found")
    q = db.query(Question).filter(Question.id == a.question_id).first()
    if q.author_id != user.id:
        raise HTTPException(status_code=403, detail="Only the question author can accept answers")
    # Unaccept any previous
    db.query(Answer).filter(Answer.question_id == q.id, Answer.is_accepted == True).update({"is_accepted": False})
    a.is_accepted = True
    db.commit()
    return {"message": "Answer accepted"}
