"""FC-3e: /api/posts — cursor-paginated unified posts feed."""

from quart import Blueprint, jsonify, request

from ..extensions import get_session
from ..models import ImportSettings, Post
from ..services import interpreter_client as ic
from ..services.post_feed_service import PostFeedService
from ..services.source_service import KNOWN_PLATFORMS
from ..utils.text import html_to_plain
from ._responses import error_response as _bad

posts_bp = Blueprint("posts", __name__, url_prefix="/api/posts")

_TRANSLATION_OVERRIDES = ("auto", "force", "original")


def _queue_for_sweep(post: Post) -> None:
    """Mark a post untranslated (all translation columns NULL) so the periodic
    sweep re-runs it under its new override — used when Interpreter is down and we
    can't translate inline."""
    post.post_title_translated = None
    post.description_translated = None
    post.translated_source_lang = None
    post.translation_engine_version = None
    post.translated_at = None


@posts_bp.route("", methods=["GET"])
async def list_posts():
    args = request.args

    cursor = args.get("cursor") or None
    artist_id_raw = args.get("artist_id")
    platform = args.get("platform") or None
    q = (args.get("q") or "").strip() or None
    limit_raw = args.get("limit", "24")
    direction = args.get("direction", "older")
    around_raw = args.get("around")

    try:
        limit = int(limit_raw)
    except ValueError:
        return _bad("invalid_limit", detail="limit must be an integer")
    if limit < 1 or limit > 100:
        return _bad("invalid_limit", detail="limit must be between 1 and 100")

    if direction not in ("older", "newer"):
        return _bad("invalid_direction", detail="direction must be 'older' or 'newer'")

    around_id = None
    if around_raw is not None:
        try:
            around_id = int(around_raw)
        except ValueError:
            return _bad("invalid_around", detail="around must be an integer post id")

    artist_id = None
    if artist_id_raw is not None:
        try:
            artist_id = int(artist_id_raw)
        except ValueError:
            return _bad("invalid_artist_id", detail="artist_id must be an integer")

    if platform is not None and platform not in KNOWN_PLATFORMS:
        return _bad(
            "unknown_platform",
            detail=f"platform must be one of {sorted(KNOWN_PLATFORMS)}",
        )

    async with get_session() as session:
        svc = PostFeedService(session)
        if around_id is not None:
            result = await svc.around(
                post_id=around_id, artist_id=artist_id,
                platform=platform, q=q, limit=limit,
            )
            if result is None:
                return _bad("not_found", status=404, detail=f"post id={around_id}")
            return jsonify(result)
        try:
            page = await svc.scroll(
                cursor=cursor, artist_id=artist_id,
                platform=platform, q=q, limit=limit, direction=direction,
            )
        except ValueError as exc:
            # Service raises ValueError for malformed cursors only;
            # limit bounds are validated above.
            return _bad("invalid_cursor", detail=str(exc))

    return jsonify(page)


@posts_bp.route("/<int:post_id>", methods=["GET"])
async def get_post(post_id: int):
    async with get_session() as session:
        item = await PostFeedService(session).get_post(post_id)
    if item is None:
        return _bad("not_found", status=404, detail=f"post id={post_id}")
    return jsonify(item)


@posts_bp.route("/<int:post_id>/translation-override", methods=["POST"])
async def set_translation_override(post_id: int):
    """Sticky per-post translation override (milestone 155). Body:
    ``{"override": "auto" | "force" | "original"}``.

    'original' keeps the original (clears any stored translation now — no
    Interpreter needed). 'force'/'auto' translate the post immediately if the
    service is up (force bypasses the acceptance floor; auto re-runs the gate);
    if it's down we save the flag and mark the post untranslated so the next sweep
    applies it. The override persists, so the sweep + Re-translate-all keep
    honoring it. Returns the updated translation fields + an ``applied`` status."""
    body = await request.get_json(silent=True) or {}
    override = body.get("override")
    if override not in _TRANSLATION_OVERRIDES:
        return _bad(
            "invalid_override",
            detail=f"override must be one of {list(_TRANSLATION_OVERRIDES)}",
        )

    # Lazy import (mirrors settings.py) so the API module doesn't pull the celery
    # task graph at import time.
    from ..tasks.translation import _store_translation, _translate_field

    async with get_session() as session:
        post = await session.get(Post, post_id)
        if post is None:
            return _bad("not_found", status=404, detail=f"post id={post_id}")
        post.translation_override = override
        cfg = await ImportSettings.load(session)
        target = (cfg.translation_target_lang or "en").strip() or "en"

        if override == "original":
            _store_translation(post, (None, None, None), (None, None, None), target)
            applied = "cleared"
        else:
            base_url = (cfg.interpreter_base_url or "").strip()
            if cfg.translation_enabled and base_url and ic.health(base_url):
                force = override == "force"
                title = (post.post_title or "").strip()
                desc = (html_to_plain(post.description) if post.description else "") or ""
                desc = desc.strip()
                mc = cfg.translation_min_confidence
                try:
                    title_res = _translate_field(title, base_url, target, mc, force=force)
                    desc_res = _translate_field(desc, base_url, target, mc, force=force)
                except ic.InterpreterUnavailable:
                    _queue_for_sweep(post)
                    applied = "queued"
                else:
                    _store_translation(post, title_res, desc_res, target)
                    applied = "translated"
            else:
                # Disabled / no URL / unhealthy → let the sweep apply it later.
                _queue_for_sweep(post)
                applied = "queued"

        await session.commit()
        return jsonify({
            "id": post.id,
            "translation_override": post.translation_override,
            "post_title_translated": post.post_title_translated,
            "description_translated": post.description_translated,
            "translated_source_lang": post.translated_source_lang,
            "applied": applied,
        })
