"""
Glossary CRUD routes for Pro users
Story 3.9: Glossaires - Endpoint CRUD
"""

import json
import logging
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import JSONResponse
from sqlalchemy.orm import joinedload

from routes.deps import require_pro_user, ProUser
from services.auth_service import verify_token, get_user_by_id
from database.connection import get_sync_session
from database.models import Glossary, GlossaryTerm
from schemas.glossary_schemas import (
    GlossaryCreate,
    GlossaryUpdate,
    GlossaryResponse,
    GlossaryListItem,
    GlossaryListResponse,
    GlossaryDetailResponse,
)

logger = logging.getLogger(__name__)

GLOSSARIES_DIR = Path(__file__).parent.parent / "data/glossaries"

router = APIRouter(prefix="/api/v1/glossaries", tags=["Glossaries v1"])

# Maximum number of terms per glossary
MAX_TERMS_PER_GLOSSARY = 500

# Default pagination
DEFAULT_PAGE_SIZE = 50
MAX_PAGE_SIZE = 100


def _format_term(term: GlossaryTerm) -> dict:
    """Format a GlossaryTerm for JSON response."""
    return {
        "id": term.id,
        "source": term.source,
        "target": term.target,
        "translations": term.translations or {},
        "created_at": term.created_at.isoformat() if term.created_at else None,
    }


def _format_glossary(glossary: Glossary) -> dict:
    """Format a Glossary for JSON response."""
    return {
        "id": glossary.id,
        "name": glossary.name,
        "source_language": glossary.source_language,
        "target_language": getattr(glossary, "target_language", "multi") or "multi",
        "template_id": getattr(glossary, "template_id", None),
        "terms": [_format_term(t) for t in glossary.terms] if glossary.terms else [],
        "created_at": glossary.created_at.isoformat() if glossary.created_at else None,
        "updated_at": glossary.updated_at.isoformat() if glossary.updated_at else None,
    }


@router.post(
    "",
    response_model=GlossaryDetailResponse,
    status_code=201,
    summary="Créer un glossaire",
    description="""
    Crée un nouveau glossaire avec une liste de termes source→cible.
    
    **Restriction:** Uniquement disponible pour les utilisateurs Pro.
    
    **Exemple de requête:**
    ```json
    {
        "name": "Glossaire Technique FR-EN",
        "terms": [
            {"source": "serveur", "target": "server"},
            {"source": "base de données", "target": "database"}
        ]
    }
    ```
    """,
)
async def create_glossary(
    body: GlossaryCreate,
    user: ProUser = Depends(require_pro_user),
):
    """Create a new glossary for the authenticated Pro user."""
    # Validate max terms
    if len(body.terms) > MAX_TERMS_PER_GLOSSARY:
        return JSONResponse(
            status_code=400,
            content={
                "error": "TERMS_LIMIT_EXCEEDED",
                "message": f"Maximum {MAX_TERMS_PER_GLOSSARY} terms per glossary allowed.",
            },
        )

    try:
        with get_sync_session() as session:
            glossary = Glossary(
                user_id=user.id,
                name=body.name,
                source_language=body.source_language,
                target_language=body.target_language,
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )

            for term_data in body.terms:
                term = GlossaryTerm(
                    glossary=glossary,
                    source=term_data.source,
                    target=term_data.target,
                    translations=term_data.translations or {},
                    created_at=datetime.now(timezone.utc),
                )
                session.add(term)

            session.add(glossary)
            session.commit()
            session.refresh(glossary)

            logger.info(
                f"Glossary created: id={glossary.id}, user_id={user.id}, "
                f"name={glossary.name}, terms_count={len(body.terms)}"
            )

            return JSONResponse(
                status_code=201,
                content={
                    "data": _format_glossary(glossary),
                    "meta": {},
                },
            )
    except Exception as e:
        logger.error(f"Failed to create glossary for user {user.id}: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "DATABASE_ERROR",
                "message": "An error occurred while creating the glossary.",
            },
        )


@router.get(
    "",
    response_model=GlossaryListResponse,
    summary="Lister les glossaires",
    description="Retourne la liste paginée des glossaires de l'utilisateur.",
)
async def list_glossaries(
    page: int = Query(1, ge=1, description="Page number"),
    per_page: int = Query(
        DEFAULT_PAGE_SIZE, ge=1, le=MAX_PAGE_SIZE, description="Items per page"
    ),
    user: ProUser = Depends(require_pro_user),
):
    """List all glossaries for the authenticated Pro user with pagination."""
    try:
        with get_sync_session() as session:
            # Get total count
            total_count = (
                session.query(Glossary).filter(Glossary.user_id == user.id).count()
            )

            # Get paginated results with eager loading of terms (fixes N+1)
            offset = (page - 1) * per_page
            glossaries = (
                session.query(Glossary)
                .options(joinedload(Glossary.terms))
                .filter(Glossary.user_id == user.id)
                .order_by(Glossary.created_at.desc())
                .offset(offset)
                .limit(per_page)
                .all()
            )

            items = [
                GlossaryListItem(
                    id=g.id,
                    name=g.name,
                    source_language=g.source_language or "fr",
                    target_language=getattr(g, "target_language", None) or "multi",
                    terms_count=len(g.terms) if g.terms else 0,
                    created_at=g.created_at,
                )
                for g in glossaries
            ]

            total_pages = (total_count + per_page - 1) // per_page

            return JSONResponse(
                status_code=200,
                content={
                    "data": [item.model_dump(mode="json") for item in items],
                    "meta": {
                        "total": total_count,
                        "page": page,
                        "per_page": per_page,
                        "total_pages": total_pages,
                    },
                },
            )
    except Exception as e:
        logger.error(f"Failed to list glossaries for user {user.id}: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "DATABASE_ERROR",
                "message": "An error occurred while retrieving glossaries.",
            },
        )


@router.get(
    "/{glossary_id}",
    response_model=GlossaryDetailResponse,
    summary="Détail d'un glossaire",
    description="Retourne les détails d'un glossaire avec tous ses termes.",
)
async def get_glossary(
    glossary_id: str,
    user: ProUser = Depends(require_pro_user),
):
    """Get a specific glossary by ID."""
    # Validate UUID format
    try:
        import uuid

        uuid.UUID(glossary_id)
    except ValueError:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_GLOSSARY_ID",
                "message": "Invalid glossary ID format.",
            },
        )

    try:
        with get_sync_session() as session:
            glossary = (
                session.query(Glossary)
                .options(joinedload(Glossary.terms))
                .filter(Glossary.id == glossary_id, Glossary.user_id == user.id)
                .first()
            )

            if not glossary:
                return JSONResponse(
                    status_code=404,
                    content={
                        "error": "GLOSSARY_NOT_FOUND",
                        "message": "Glossary not found or you do not have access to this resource.",
                    },
                )

            return JSONResponse(
                status_code=200,
                content={
                    "data": _format_glossary(glossary),
                    "meta": {},
                },
            )
    except Exception as e:
        logger.error(f"Failed to get glossary {glossary_id}: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "DATABASE_ERROR",
                "message": "An error occurred.",
            },
        )


@router.patch(
    "/{glossary_id}",
    response_model=GlossaryDetailResponse,
    summary="Mettre à jour un glossaire",
    description="Met à jour le nom et/ou les termes d'un glossaire existant.",
)
async def update_glossary(
    glossary_id: str,
    body: GlossaryUpdate,
    user: ProUser = Depends(require_pro_user),
):
    """Update a glossary's name and/or terms."""
    # Validate UUID format
    try:
        import uuid

        uuid.UUID(glossary_id)
    except ValueError:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_GLOSSARY_ID",
                "message": "Invalid glossary ID format.",
            },
        )

    # Validate max terms if provided
    if body.terms is not None and len(body.terms) > MAX_TERMS_PER_GLOSSARY:
        return JSONResponse(
            status_code=400,
            content={
                "error": "TERMS_LIMIT_EXCEEDED",
                "message": f"Maximum {MAX_TERMS_PER_GLOSSARY} terms per glossary allowed.",
            },
        )

    try:
        with get_sync_session() as session:
            glossary = (
                session.query(Glossary)
                .options(joinedload(Glossary.terms))
                .filter(Glossary.id == glossary_id, Glossary.user_id == user.id)
                .first()
            )

            if not glossary:
                return JSONResponse(
                    status_code=404,
                    content={
                        "error": "GLOSSARY_NOT_FOUND",
                        "message": "Glossary not found or you do not have access to this resource.",
                    },
                )

            old_name = glossary.name

            if body.name is not None:
                glossary.name = body.name

            if body.source_language is not None:
                glossary.source_language = body.source_language

            if body.target_language is not None:
                glossary.target_language = body.target_language
            if body.terms is not None:
                # Delete existing terms
                session.query(GlossaryTerm).filter(
                    GlossaryTerm.glossary_id == glossary.id
                ).delete()

                # Add new terms
                for term_data in body.terms:
                    term = GlossaryTerm(
                        glossary_id=glossary.id,
                        source=term_data.source,
                        target=term_data.target,
                        translations=term_data.translations or {},
                        created_at=datetime.now(timezone.utc),
                    )
                    session.add(term)

            glossary.updated_at = datetime.now(timezone.utc)
            session.commit()
            session.refresh(glossary)

            logger.info(
                f"Glossary updated: id={glossary.id}, user_id={user.id}, "
                f"old_name={old_name}, new_name={glossary.name}"
            )

            return JSONResponse(
                status_code=200,
                content={
                    "data": _format_glossary(glossary),
                    "meta": {},
                },
            )
    except Exception as e:
        logger.error(f"Failed to update glossary {glossary_id}: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "DATABASE_ERROR",
                "message": "An error occurred while updating.",
            },
        )


@router.delete(
    "/{glossary_id}",
    status_code=204,
    summary="Supprimer un glossaire",
    description="Supprime un glossaire et tous ses termes associés.",
)
async def delete_glossary(
    glossary_id: str,
    user: ProUser = Depends(require_pro_user),
):
    """Delete a glossary and all its terms."""
    # Validate UUID format
    try:
        import uuid

        uuid.UUID(glossary_id)
    except ValueError:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_GLOSSARY_ID",
                "message": "Invalid glossary ID format.",
            },
        )

    try:
        with get_sync_session() as session:
            glossary = (
                session.query(Glossary)
                .filter(Glossary.id == glossary_id, Glossary.user_id == user.id)
                .first()
            )

            if not glossary:
                return JSONResponse(
                    status_code=404,
                    content={
                        "error": "GLOSSARY_NOT_FOUND",
                        "message": "Glossary not found or you do not have access to this resource.",
                    },
                )

            glossary_name = glossary.name
            session.delete(glossary)
            session.commit()

            logger.info(
                f"Glossary deleted: id={glossary_id}, user_id={user.id}, "
                f"name={glossary_name}"
            )

            return JSONResponse(
                status_code=204,
                content=None,
            )
    except Exception as e:
        logger.error(f"Failed to delete glossary {glossary_id}: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "DATABASE_ERROR",
                "message": "An error occurred while deleting.",
            },
        )


@router.get(
    "/templates/list",
    summary="Lister les templates de glossaires",
    description="""
    Retourne la liste des glossaires pré-définis disponibles (templates).
    
    Ces templates couvrent différents domaines : juridique, technologie, finance, médical, marketing, RH, scientifique, e-commerce.
    
    Utilisez ensuite `POST /glossaries/import` pour importer un template dans votre compte.
    """,
)
async def list_glossary_templates():
    """List all available glossary templates."""
    try:
        index_path = GLOSSARIES_DIR / "index.json"
        if not index_path.exists():
            return JSONResponse(
                status_code=404,
                content={
                    "error": "TEMPLATES_NOT_FOUND",
                    "message": "Glossary templates are not available.",
                },
            )
        
        with open(index_path, "r", encoding="utf-8") as f:
            index_data = json.load(f)
        
        templates = []
        for category_id, category_data in index_data.get("categories", {}).items():
            templates.append({
                "id": category_id,
                "name": category_data.get("name", category_id),
                "description": category_data.get("description", ""),
                "source_lang": category_data.get("source_lang", "fr"),
                "target_lang": category_data.get("target_lang", "en"),
                "terms_count": category_data.get("terms_count", 0),
                "file": category_data.get("file", f"{category_id}_fr_en.json"),
            })
        
        return JSONResponse(
            status_code=200,
            content={
                "data": templates,
                "meta": {
                    "total": len(templates),
                },
            },
        )
    except Exception as e:
        logger.error(f"Failed to list glossary templates: {e}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "TEMPLATES_ERROR",
                "message": "An error occurred while retrieving templates.",
            },
        )


@router.post(
    "/import",
    response_model=GlossaryDetailResponse,
    status_code=201,
    summary="Importer un template de glossaire",
    description="""
    Importe un glossaire pré-défini dans votre compte.
    
    **Paramètres:**
    - `template_id`: L'identifiant du template (ex: "legal", "tech", "finance", "medical", "marketing", "hr", "scientific", "ecommerce")
    - `name` (optionnel): Nom personnalisé pour le glossaire. Si non fourni, le nom du template sera utilisé.
    
    **Exemple:**
    ```json
    {
        "template_id": "legal",
        "name": "Mon glossaire juridique"
    }
    ```
    """,
)
async def import_glossary_template(
    template_id: str = Query(..., description="ID du template à importer"),
    name: Optional[str] = Query(None, description="Nom personnalisé pour le glossaire"),
    user: ProUser = Depends(require_pro_user),
):
    """Import a pre-built glossary template into the user's account."""
    try:
        index_path = GLOSSARIES_DIR / "index.json"
        if not index_path.exists():
            return JSONResponse(
                status_code=404,
                content={
                    "error": "TEMPLATES_NOT_FOUND",
                    "message": "Glossary templates are not available.",
                },
            )
        
        with open(index_path, "r", encoding="utf-8") as f:
            index_data = json.load(f)
        
        categories = index_data.get("categories", {})
        if template_id not in categories:
            return JSONResponse(
                status_code=404,
                content={
                    "error": "TEMPLATE_NOT_FOUND",
                    "message": f"Template '{template_id}' not found. Available templates: {', '.join(categories.keys())}",
                },
            )
        
        template_info = categories[template_id]
        template_file = template_info.get("file", f"{template_id}_fr_en.json")
        template_path = GLOSSARIES_DIR / template_file
        
        if not template_path.exists():
            return JSONResponse(
                status_code=404,
                content={
                    "error": "TEMPLATE_FILE_NOT_FOUND",
                    "message": f"Template file '{template_file}' not found.",
                },
            )
        
        with open(template_path, "r", encoding="utf-8") as f:
            template_data = json.load(f)
        
        terms = template_data.get("terms", [])
        if len(terms) > MAX_TERMS_PER_GLOSSARY:
            return JSONResponse(
                status_code=400,
                content={
                    "error": "TERMS_LIMIT_EXCEEDED",
                    "message": f"The template contains {len(terms)} terms, exceeding the limit of {MAX_TERMS_PER_GLOSSARY}.",
                },
            )
        
        glossary_name = name or template_data.get("name", template_info.get("name", template_id))

        with get_sync_session() as session:
            # Check if user already imported this template
            existing = (
                session.query(Glossary)
                .filter_by(user_id=user.id, template_id=template_id)
                .first()
            )
            if existing:
                return JSONResponse(
                    status_code=409,
                    content={
                        "error": "TEMPLATE_ALREADY_IMPORTED",
                        "message": f"You already have a glossary from the '{template_id}' template.",
                        "data": _format_glossary(existing),
                    },
                )

            glossary = Glossary(
                user_id=user.id,
                name=glossary_name,
                source_language=template_info.get("source_lang", template_data.get("source_lang", "fr")),
                target_language=template_info.get("target_lang", template_data.get("target_lang", "multi")),
                template_id=template_id,
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )
            
            for term_data in terms:
                term = GlossaryTerm(
                    glossary=glossary,
                    source=term_data.get("source", ""),
                    target=term_data.get("target", ""),
                    translations=term_data.get("translations") or None,
                    created_at=datetime.now(timezone.utc),
                )
                session.add(term)
            
            session.add(glossary)
            session.commit()
            session.refresh(glossary)
            
            logger.info(
                f"Glossary template imported: id={glossary.id}, user_id={user.id}, "
                f"template={template_id}, name={glossary_name}, terms_count={len(terms)}"
            )
            
            return JSONResponse(
                status_code=201,
                content={
                    "data": _format_glossary(glossary),
                    "meta": {
                        "template_id": template_id,
                        "imported_terms": len(terms),
                    },
                },
            )
    except Exception as e:
        logger.error(f"Failed to import glossary template {template_id}: {e}", exc_info=True)
        return JSONResponse(
            status_code=500,
            content={
                "error": "IMPORT_ERROR",
                "message": f"Import failed: {str(e)}",
            },
        )
