"""Admin inbox notifications for member activity (claims, tracks, community)."""

from __future__ import annotations

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

from fastapi import HTTPException, status
from sqlalchemy import inspect, text
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session, joinedload

from app.models import User
from app.models_admin import AdminNotification, AdminNotificationType

_ADMIN_NOTIFICATION_TYPE_LABELS: dict[AdminNotificationType, str] = {
    AdminNotificationType.reward_claim: "Reward claim",
    AdminNotificationType.track_submit: "Track submitted",
    AdminNotificationType.community_like: "Community like",
    AdminNotificationType.community_comment: "Community comment",
    AdminNotificationType.community_share: "Community share",
}

_CREATE_ADMIN_NOTIFICATIONS_PG = """
CREATE TABLE IF NOT EXISTS admin_notifications (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    notification_type VARCHAR(64) NOT NULL,
    title VARCHAR(255) NOT NULL,
    message TEXT NOT NULL,
    reference_type VARCHAR(64),
    reference_id INTEGER,
    is_read BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_admin_notifications_user_id ON admin_notifications (user_id);
CREATE INDEX IF NOT EXISTS ix_admin_notifications_notification_type ON admin_notifications (notification_type);
CREATE INDEX IF NOT EXISTS ix_admin_notifications_id ON admin_notifications (id);
"""


def sync_admin_notifications_schema(engine: Engine) -> None:
    is_pg = engine.dialect.name == "postgresql"
    try:
        insp = inspect(engine)
        table_exists = "admin_notifications" in insp.get_table_names()
    except Exception:
        table_exists = False

    if not table_exists and is_pg:
        with engine.begin() as conn:
            conn.execute(text(_CREATE_ADMIN_NOTIFICATIONS_PG))
    elif not table_exists:
        from app import models_admin  # noqa: F401
        from app.database import Base

        Base.metadata.tables["admin_notifications"].create(bind=engine, checkfirst=True)


def _format_notification_datetime(dt: datetime | None) -> str | None:
    if dt is None:
        return None
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    return dt.astimezone(ZoneInfo("Europe/London")).strftime("%d %b, %Y %I:%M %p").lstrip("0")


def _type_label(notification_type: str) -> str:
    try:
        return _ADMIN_NOTIFICATION_TYPE_LABELS[AdminNotificationType(notification_type)]
    except ValueError:
        return notification_type.replace("_", " ").title()


def serialize_admin_notification(row: AdminNotification) -> dict:
    user_name = row.user.full_name.strip() if row.user and row.user.full_name else None
    if not user_name and row.user:
        user_name = row.user.email
    if not user_name:
        user_name = f"User #{row.user_id}"
    return {
        "id": row.id,
        "user_id": row.user_id,
        "user_name": user_name,
        "member_name": user_name,
        "notification_type": row.notification_type,
        "type_label": _type_label(row.notification_type),
        "title": row.title,
        "message": row.message,
        "reference_type": row.reference_type,
        "reference_id": row.reference_id,
        "is_read": row.is_read,
        "created_at": row.created_at,
        "created_at_label": _format_notification_datetime(row.created_at),
    }


def create_admin_notification(
    db: Session,
    *,
    user: User,
    notification_type: AdminNotificationType | str,
    title: str,
    message: str,
    reference_type: str | None = None,
    reference_id: int | None = None,
    commit: bool = True,
) -> AdminNotification:
    ntype = (
        notification_type
        if isinstance(notification_type, AdminNotificationType)
        else AdminNotificationType(str(notification_type).strip().lower())
    )
    cleaned_title = title.strip()
    cleaned_message = message.strip()
    if not cleaned_title or not cleaned_message:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Notification title and message are required",
        )

    row = AdminNotification(
        user_id=user.id,
        notification_type=ntype.value,
        title=cleaned_title,
        message=cleaned_message,
        reference_type=reference_type,
        reference_id=reference_id,
    )
    db.add(row)
    if commit:
        db.commit()
        db.refresh(row)
    else:
        db.flush()
    return row


def list_admin_notifications(db: Session) -> list[AdminNotification]:
    return (
        db.query(AdminNotification)
        .options(joinedload(AdminNotification.user))
        .order_by(AdminNotification.created_at.desc(), AdminNotification.id.desc())
        .all()
    )


def count_unread_admin_notifications(db: Session) -> int:
    return db.query(AdminNotification).filter(AdminNotification.is_read.is_(False)).count()


def mark_admin_notification_read(db: Session, notification_id: int) -> AdminNotification | None:
    row = db.get(AdminNotification, notification_id)
    if not row:
        return None
    row.is_read = True
    db.commit()
    db.refresh(row)
    return row


def mark_all_admin_notifications_read(db: Session) -> int:
    count = (
        db.query(AdminNotification)
        .filter(AdminNotification.is_read.is_(False))
        .update({AdminNotification.is_read: True}, synchronize_session=False)
    )
    db.commit()
    return count


def delete_admin_notification(db: Session, notification_id: int) -> bool:
    row = db.get(AdminNotification, notification_id)
    if not row:
        return False
    db.delete(row)
    db.commit()
    return True


def clear_all_admin_notifications(db: Session) -> int:
    count = db.query(AdminNotification).count()
    if count:
        db.query(AdminNotification).delete(synchronize_session=False)
        db.commit()
    return count


def _member_display_name(user: User) -> str:
    return (user.full_name or "").strip() or user.email


def notify_admin_reward_claim(db: Session, user: User, *, product_name: str, points: int, claim_id: int) -> None:
    name = _member_display_name(user)
    create_admin_notification(
        db,
        user=user,
        notification_type=AdminNotificationType.reward_claim,
        title="New reward claim",
        message=f"{name} submitted a reward claim for {product_name} ({points} points).",
        reference_type="reward_claim",
        reference_id=claim_id,
    )


def notify_admin_track_submit(
    db: Session,
    user: User,
    *,
    step_label: str,
    reference_id: int | None = None,
) -> None:
    name = _member_display_name(user)
    create_admin_notification(
        db,
        user=user,
        notification_type=AdminNotificationType.track_submit,
        title="Track progress submitted",
        message=f"{name} completed a track step: {step_label}.",
        reference_type="track",
        reference_id=reference_id,
    )


def notify_admin_community_like(db: Session, user: User, *, post_id: int, post_title: str) -> None:
    name = _member_display_name(user)
    title = (post_title or "").strip() or f"Post #{post_id}"
    create_admin_notification(
        db,
        user=user,
        notification_type=AdminNotificationType.community_like,
        title="Community post liked",
        message=f'{name} liked "{title}".',
        reference_type="community_post",
        reference_id=post_id,
    )


def notify_admin_community_comment(
    db: Session, user: User, *, post_id: int, post_title: str, comment_preview: str
) -> None:
    name = _member_display_name(user)
    title = (post_title or "").strip() or f"Post #{post_id}"
    preview = comment_preview.strip()
    if len(preview) > 120:
        preview = f"{preview[:117]}..."
    create_admin_notification(
        db,
        user=user,
        notification_type=AdminNotificationType.community_comment,
        title="New community comment",
        message=f'{name} commented on "{title}": {preview}',
        reference_type="community_post",
        reference_id=post_id,
    )


def notify_admin_community_share(
    db: Session, user: User, *, post_id: int, post_title: str, platform: str
) -> None:
    name = _member_display_name(user)
    title = (post_title or "").strip() or f"Post #{post_id}"
    create_admin_notification(
        db,
        user=user,
        notification_type=AdminNotificationType.community_share,
        title="Community post shared",
        message=f'{name} shared "{title}" on {platform}.',
        reference_type="community_post",
        reference_id=post_id,
    )
