import os
import uuid
import re
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, 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.publication import Publication, PublicationFile, PublicationComment, Citation
from app.models.tag import Tag
from app.schemas import PublicationCreate, CommentCreate
from app.services.auth import require_user, get_current_user
from app.services.notifications import notify_followers
from app.config import settings

router = APIRouter(prefix="/api/publications", tags=["publications"])


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_publication(
    title: str = Form(...),
    abstract: str = Form(None),
    content: str = Form(None),
    doi: str = Form(None),
    publication_type: str = Form("article"),
    journal: str = Form(None),
    year: int = Form(None),
    git_repo_url: str = Form(None),
    video_url: str = Form(None),
    field_id: int = Form(None),
    tags: str = Form(""),
    citations: str = Form(""),
    files: list[UploadFile] = File(default=[]),
    user: User = Depends(require_user),
    db: Session = Depends(get_db),
):
    pub = Publication(
        title=title, abstract=abstract, content=content, doi=doi,
        publication_type=publication_type, journal=journal, year=year,
        git_repo_url=git_repo_url, video_url=video_url,
        field_id=field_id, author_id=user.id,
    )
    # Tags
    if tags:
        for tag_name in tags.split(","):
            tag_name = tag_name.strip()
            if tag_name:
                pub.tags.append(get_or_create_tag(db, tag_name))
    db.add(pub)
    db.flush()

    # Citations
    if citations:
        for citation_text in citations.split("\n"):
            citation_text = citation_text.strip()
            if citation_text:
                cit = Citation(publication_id=pub.id, citation_text=citation_text)
                db.add(cit)

    # File uploads
    for upload_file in files:
        if upload_file.filename:
            ext = os.path.splitext(upload_file.filename)[1].lower()
            file_type = "pdf" if ext == ".pdf" else "image" if ext in (".jpg", ".jpeg", ".png", ".gif", ".webp") else "video" if ext in (".mp4", ".webm", ".avi") else "other"
            safe_name = f"{uuid.uuid4().hex}{ext}"
            file_path = os.path.join(settings.UPLOAD_DIR, "publications", safe_name)
            with open(file_path, "wb") as f:
                f.write(upload_file.file.read())
            pub_file = PublicationFile(
                publication_id=pub.id,
                file_path=f"/uploads/publications/{safe_name}",
                file_type=file_type,
                original_name=upload_file.filename,
                file_size=os.path.getsize(file_path),
            )
            db.add(pub_file)

    db.commit()
    db.refresh(pub)
    return {"id": pub.id, "title": pub.title}


@router.get("")
def list_publications(
    q: str = Query(""),
    field_id: int | None = Query(None),
    tag: str | None = Query(None),
    author: str | None = Query(None),
    year_from: int | None = Query(None),
    year_to: int | 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(Publication).options(
        joinedload(Publication.author),
        joinedload(Publication.field),
        joinedload(Publication.tags),
    )
    if q:
        query = query.filter(or_(
            Publication.title.ilike(f"%{q}%"),
            Publication.abstract.ilike(f"%{q}%"),
        ))
    if field_id:
        query = query.filter(Publication.field_id == field_id)
    if tag:
        query = query.join(Publication.tags).filter(Tag.slug == tag)
    if author:
        query = query.join(Publication.author).filter(
            or_(User.full_name.ilike(f"%{author}%"), User.username.ilike(f"%{author}%"))
        )
    if year_from:
        query = query.filter(Publication.year >= year_from)
    if year_to:
        query = query.filter(Publication.year <= year_to)

    if sort == "oldest":
        query = query.order_by(Publication.created_at.asc())
    elif sort == "popular":
        query = query.order_by(Publication.view_count.desc())
    elif sort == "cited":
        query = query.order_by(Publication.citation_count.desc())
    else:
        query = query.order_by(Publication.created_at.desc())

    total = query.count()
    pubs = query.offset((page - 1) * per_page).limit(per_page).all()
    return {
        "total": total, "page": page, "per_page": per_page,
        "results": [
            {
                "id": p.id, "title": p.title, "abstract": p.abstract,
                "publication_type": p.publication_type, "journal": p.journal,
                "year": p.year, "view_count": p.view_count, "citation_count": p.citation_count,
                "doi": p.doi, "created_at": str(p.created_at),
                "author": {"id": p.author.id, "full_name": p.author.full_name, "username": p.author.username, "is_verified": p.author.is_verified} if p.author else None,
                "field": {"id": p.field.id, "name": p.field.name, "slug": p.field.slug, "color": p.field.color} if p.field else None,
                "tags": [{"name": t.name, "slug": t.slug} for t in p.tags],
            }
            for p in pubs
        ],
    }


@router.get("/{pub_id}")
def get_publication(pub_id: int, db: Session = Depends(get_db)):
    pub = db.query(Publication).options(
        joinedload(Publication.author),
        joinedload(Publication.field),
        joinedload(Publication.tags),
        joinedload(Publication.files),
        joinedload(Publication.comments).joinedload(PublicationComment.author),
        joinedload(Publication.citations),
    ).filter(Publication.id == pub_id).first()
    if not pub:
        raise HTTPException(status_code=404, detail="Publication not found")
    pub.view_count += 1
    db.commit()
    return {
        "id": pub.id, "title": pub.title, "abstract": pub.abstract, "content": pub.content,
        "doi": pub.doi, "publication_type": pub.publication_type, "journal": pub.journal,
        "year": pub.year, "git_repo_url": pub.git_repo_url, "video_url": pub.video_url,
        "view_count": pub.view_count, "citation_count": pub.citation_count,
        "created_at": str(pub.created_at),
        "author": {
            "id": pub.author.id, "full_name": pub.author.full_name, "username": pub.author.username,
            "is_verified": pub.author.is_verified, "avatar_url": pub.author.avatar_url,
            "affiliation": {"designation": pub.author.primary_affiliation.designation, "institution": pub.author.primary_affiliation.institution} if pub.author.primary_affiliation else None,
        },
        "field": {"id": pub.field.id, "name": pub.field.name, "slug": pub.field.slug, "color": pub.field.color} if pub.field else None,
        "tags": [{"name": t.name, "slug": t.slug} for t in pub.tags],
        "files": [{"id": f.id, "file_path": f.file_path, "file_type": f.file_type, "original_name": f.original_name} for f in pub.files],
        "citations": [{"id": c.id, "citation_text": c.citation_text} for c in pub.citations],
        "comments": [
            {
                "id": c.id, "content": c.content, "parent_id": c.parent_id,
                "created_at": str(c.created_at),
                "author": {"id": c.author.id, "full_name": c.author.full_name, "username": c.author.username, "is_verified": c.author.is_verified},
            }
            for c in sorted(pub.comments, key=lambda x: x.created_at)
        ],
    }


@router.post("/{pub_id}/comments")
def add_comment(pub_id: int, data: CommentCreate, user: User = Depends(require_user), db: Session = Depends(get_db)):
    pub = db.query(Publication).filter(Publication.id == pub_id).first()
    if not pub:
        raise HTTPException(status_code=404, detail="Publication not found")
    comment = PublicationComment(
        publication_id=pub_id, author_id=user.id,
        content=data.content, parent_id=data.parent_id,
    )
    db.add(comment)
    db.commit()
    # Notify publication author and followers
    if pub.author_id != user.id:
        notify_followers(db, "publication", pub_id, user.id, "comment",
                         f"{user.full_name} commented on your publication \"{pub.title}\"",
                         f"/publications/{pub_id}")
    return {"id": comment.id, "message": "Comment added"}


@router.delete("/{pub_id}")
def delete_publication(pub_id: int, user: User = Depends(require_user), db: Session = Depends(get_db)):
    pub = db.query(Publication).filter(Publication.id == pub_id).first()
    if not pub:
        raise HTTPException(status_code=404, detail="Publication not found")
    if pub.author_id != user.id and not user.is_admin:
        raise HTTPException(status_code=403, detail="Not authorized")
    db.delete(pub)
    db.commit()
    return {"message": "Deleted"}
