"""API endpoints for reblog controls (export gate filters)."""

from sqlalchemy import select
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.responses import Response
from starlette.routing import Route

from fenliu.database import get_db
from fenliu.models import AppSetting
from fenliu.models import BlockedHashtag
from fenliu.models import BlockedUser
from fenliu.schemas import BlockedHashtagCreate
from fenliu.schemas import BlockedHashtagResponse
from fenliu.schemas import BlockedUserCreate
from fenliu.schemas import BlockedUserResponse
from fenliu.schemas import ReblogSettingsResponse
from fenliu.schemas import ReblogSettingsUpdate
from fenliu.services.export_eligibility import SETTING_ATTACHMENTS_ONLY
from fenliu.services.export_eligibility import SETTING_AUTO_REJECT_BLOCKED
from fenliu.services.export_eligibility import SETTING_AUTO_REJECT_BOTS
from fenliu.services.export_eligibility import get_auto_reject_bots_setting
from fenliu.services.export_eligibility import get_auto_reject_setting
from fenliu.services.export_eligibility import reject_blocked_posts


def _get_bool_setting(db, key: str) -> bool:
    """Read a boolean AppSetting by key (default False)."""
    row = db.execute(select(AppSetting).where(AppSetting.key == key)).scalar_one_or_none()
    if row is None:
        return False
    return row.value.lower() == "true"


def _set_bool_setting(db, key: str, value: bool) -> None:
    """Upsert a boolean AppSetting."""
    row = db.execute(select(AppSetting).where(AppSetting.key == key)).scalar_one_or_none()
    if row is None:
        db.add(AppSetting(key=key, value=str(value).lower()))
    else:
        row.value = str(value).lower()


def get_attachments_only_setting(db) -> bool:
    """Read the attachments_only global setting from the database."""
    return _get_bool_setting(db, SETTING_ATTACHMENTS_ONLY)


async def list_blocked_users(_request: Request) -> JSONResponse:
    """Return all blocked users."""
    with get_db() as db:
        stmt = select(BlockedUser).order_by(BlockedUser.created_at.desc())
        rows = list(db.execute(stmt).scalars().all())
        return JSONResponse([BlockedUserResponse.model_validate(r).model_dump(mode="json") for r in rows])


async def add_blocked_user(request: Request) -> JSONResponse:
    """Add a user to the 'Don't Reblog' blocklist."""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse({"detail": "Invalid JSON body"}, status_code=400)

    try:
        payload = BlockedUserCreate.model_validate(body)
    except Exception as exc:
        return JSONResponse({"detail": str(exc)}, status_code=422)

    # Normalise: strip whitespace and validate pattern type
    identifier = payload.account_identifier.strip()
    pattern_type = getattr(payload, "pattern_type", "exact")

    # Validate pattern_type
    valid_types = {"exact", "suffix", "prefix", "contains"}
    if pattern_type not in valid_types:
        return JSONResponse(
            {"detail": f"Invalid pattern_type. Must be one of: {', '.join(valid_types)}"}, status_code=422
        )

    with get_db() as db:
        # Check for duplicate (same identifier + pattern_type combination)
        existing = db.execute(
            select(BlockedUser).where(
                (BlockedUser.account_identifier == identifier) & (BlockedUser.pattern_type == pattern_type)
            )
        ).scalar_one_or_none()
        if existing is not None:
            return JSONResponse({"detail": "This pattern is already in the blocklist"}, status_code=409)

        blocked = BlockedUser(account_identifier=identifier, pattern_type=pattern_type, notes=payload.notes)
        db.add(blocked)
        db.commit()
        db.refresh(blocked)
        return JSONResponse(
            BlockedUserResponse.model_validate(blocked).model_dump(mode="json"),
            status_code=201,
        )


async def delete_blocked_user(request: Request) -> Response:
    """Remove a user from the blocklist."""
    try:
        user_id: int = int(request.path_params["user_id"])
    except (KeyError, ValueError):
        return JSONResponse({"detail": "Invalid user ID"}, status_code=400)

    with get_db() as db:
        row = db.get(BlockedUser, user_id)
        if row is None:
            return JSONResponse({"detail": "Blocked user not found"}, status_code=404)
        db.delete(row)
        db.commit()
    return Response(status_code=204)


async def list_blocked_hashtags(_request: Request) -> JSONResponse:
    """Return all blocked hashtags."""
    with get_db() as db:
        stmt = select(BlockedHashtag).order_by(BlockedHashtag.created_at.desc())
        rows = list(db.execute(stmt).scalars().all())
        return JSONResponse([BlockedHashtagResponse.model_validate(r).model_dump(mode="json") for r in rows])


async def add_blocked_hashtag(request: Request) -> JSONResponse:
    """Add a hashtag to the 'Don't Reblog' blocklist."""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse({"detail": "Invalid JSON body"}, status_code=400)

    try:
        payload = BlockedHashtagCreate.model_validate(body)
    except Exception as exc:
        return JSONResponse({"detail": str(exc)}, status_code=422)

    # Normalise: strip leading # and whitespace, lowercase
    hashtag = payload.hashtag.strip().lstrip("#").lower()
    if not hashtag:
        return JSONResponse({"detail": "Hashtag must not be empty after normalisation"}, status_code=422)

    with get_db() as db:
        existing = db.execute(select(BlockedHashtag).where(BlockedHashtag.hashtag == hashtag)).scalar_one_or_none()
        if existing is not None:
            return JSONResponse({"detail": "Hashtag already in blocklist"}, status_code=409)

        blocked = BlockedHashtag(hashtag=hashtag, notes=payload.notes)
        db.add(blocked)
        db.commit()
        db.refresh(blocked)
        return JSONResponse(
            BlockedHashtagResponse.model_validate(blocked).model_dump(mode="json"),
            status_code=201,
        )


async def delete_blocked_hashtag(request: Request) -> Response:
    """Remove a hashtag from the blocklist."""
    try:
        hashtag_id: int = int(request.path_params["hashtag_id"])
    except (KeyError, ValueError):
        return JSONResponse({"detail": "Invalid hashtag ID"}, status_code=400)

    with get_db() as db:
        row = db.get(BlockedHashtag, hashtag_id)
        if row is None:
            return JSONResponse({"detail": "Blocked hashtag not found"}, status_code=404)
        db.delete(row)
        db.commit()
    return Response(status_code=204)


async def get_reblog_settings(_request: Request) -> JSONResponse:
    """Return current global reblog export settings."""
    with get_db() as db:
        resp = ReblogSettingsResponse(
            attachments_only=get_attachments_only_setting(db),
            auto_reject_on_fetch=get_auto_reject_setting(db),
            auto_reject_bots=get_auto_reject_bots_setting(db),
        )
    return JSONResponse(resp.model_dump())


async def update_reblog_settings(request: Request) -> JSONResponse:
    """Update global reblog export settings."""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse({"detail": "Invalid JSON body"}, status_code=400)

    try:
        payload = ReblogSettingsUpdate.model_validate(body)
    except Exception as exc:
        return JSONResponse({"detail": str(exc)}, status_code=422)

    with get_db() as db:
        _set_bool_setting(db, SETTING_ATTACHMENTS_ONLY, payload.attachments_only)
        _set_bool_setting(db, SETTING_AUTO_REJECT_BLOCKED, payload.auto_reject_on_fetch)
        _set_bool_setting(db, SETTING_AUTO_REJECT_BOTS, payload.auto_reject_bots)
        db.commit()

    return JSONResponse(
        ReblogSettingsResponse(
            attachments_only=payload.attachments_only,
            auto_reject_on_fetch=payload.auto_reject_on_fetch,
            auto_reject_bots=payload.auto_reject_bots,
        ).model_dump()
    )


async def reject_blocked(_request: Request) -> JSONResponse:
    """Reject all unreviewed posts that match any active reblog-control filter."""
    with get_db() as db:
        count = reject_blocked_posts(db)
        db.commit()
    return JSONResponse({"rejected": count})


reblog_controls_routes = [
    Route("/blocked-users", list_blocked_users, methods=["GET"]),
    Route("/blocked-users", add_blocked_user, methods=["POST"]),
    Route("/blocked-users/{user_id:int}", delete_blocked_user, methods=["DELETE"]),
    Route("/blocked-hashtags", list_blocked_hashtags, methods=["GET"]),
    Route("/blocked-hashtags", add_blocked_hashtag, methods=["POST"]),
    Route("/blocked-hashtags/{hashtag_id:int}", delete_blocked_hashtag, methods=["DELETE"]),
    Route("/settings", get_reblog_settings, methods=["GET"]),
    Route("/settings", update_reblog_settings, methods=["PUT"]),
    Route("/reject-blocked", reject_blocked, methods=["POST"]),
]
