"""
Acceso a datos del foro (BD ``acore_web``), con SQL directo — mismo estilo que
el resto del proyecto (``connections['acore_web']``).

Tablas: forum_categories, forums, forum_topics, forum_posts, forum_reads.
"""

from django.db import connections


def _dictfetchall(cursor):
    cols = [c[0] for c in cursor.description]
    return [dict(zip(cols, row)) for row in cursor.fetchall()]


def _dictfetchone(cursor):
    row = cursor.fetchone()
    if not row:
        return None
    cols = [c[0] for c in cursor.description]
    return dict(zip(cols, row))


def _conn():
    return connections['acore_web']


# Detección perezosa de la columna opcional forum_topics.views (contador de visitas)
_views_supported = None


def has_views_column():
    global _views_supported
    if _views_supported is None:
        try:
            with _conn().cursor() as cursor:
                cursor.execute("SELECT views FROM forum_topics LIMIT 1")
            _views_supported = True
        except Exception:
            _views_supported = False
    return _views_supported


def increment_views(topic_id):
    """Suma 1 a las visitas del tema (si existe la columna)."""
    if not has_views_column():
        return
    try:
        with _conn().cursor() as cursor:
            cursor.execute(
                "UPDATE forum_topics SET views = views + 1 WHERE id = %s", [topic_id]
            )
    except Exception:
        pass


# --------------------------------------------------------------------------
# Portada: categorías + foros (con estadísticas)
# --------------------------------------------------------------------------

def get_index(include_hidden=False):
    """
    Devuelve las categorías ordenadas, cada una con sus foros (visibles y no
    borrados salvo include_hidden) y estadísticas: nº de temas, nº de posts y
    último tema.
    """
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT id, `order`, name FROM forum_categories ORDER BY `order`, id"
        )
        categories = _dictfetchall(cursor)

        vis = "" if include_hidden else "AND f.visibility = 1 AND f.deleted = 0"
        cursor.execute(
            f"""
            SELECT f.id, f.category_id, f.`order`, f.name, f.description, f.icon,
                   f.colortitle, f.type, f.visibility, f.deleted,
                   (SELECT COUNT(*) FROM forum_topics t
                      WHERE t.forum_id = f.id AND t.deleted = 0) AS topic_count,
                   (SELECT COUNT(*) FROM forum_posts p
                      JOIN forum_topics t ON p.topic_id = t.id
                      WHERE t.forum_id = f.id AND p.deleted = 0 AND t.deleted = 0) AS post_count,
                   (SELECT t2.id FROM forum_topics t2
                      WHERE t2.forum_id = f.id AND t2.deleted = 0
                      ORDER BY t2.updated_at DESC, t2.id DESC LIMIT 1) AS last_topic_id,
                   (SELECT t3.name FROM forum_topics t3
                      WHERE t3.forum_id = f.id AND t3.deleted = 0
                      ORDER BY t3.updated_at DESC, t3.id DESC LIMIT 1) AS last_topic_name,
                   (SELECT t4.poster FROM forum_topics t4
                      WHERE t4.forum_id = f.id AND t4.deleted = 0
                      ORDER BY t4.updated_at DESC, t4.id DESC LIMIT 1) AS last_topic_poster,
                   (SELECT t5.updated_at FROM forum_topics t5
                      WHERE t5.forum_id = f.id AND t5.deleted = 0
                      ORDER BY t5.updated_at DESC, t5.id DESC LIMIT 1) AS last_topic_time
            FROM forums f
            WHERE 1=1 {vis}
            ORDER BY f.`order`, f.id
            """
        )
        forums = _dictfetchall(cursor)

    by_cat = {}
    for f in forums:
        by_cat.setdefault(f['category_id'], []).append(f)
    for c in categories:
        c['forums'] = by_cat.get(c['id'], [])
    # Solo categorías con foros visibles (salvo include_hidden)
    if not include_hidden:
        categories = [c for c in categories if c['forums']]
    return categories


# --------------------------------------------------------------------------
# Foro individual y sus temas (paginado)
# --------------------------------------------------------------------------

def get_forum(forum_id, include_hidden=False):
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT id, category_id, `order`, name, description, icon, colortitle, "
            "type, visibility, deleted FROM forums WHERE id = %s",
            [forum_id],
        )
        forum = _dictfetchone(cursor)
    if not forum:
        return None
    if not include_hidden and (forum['deleted'] or not forum['visibility']):
        return None
    return forum


def count_topics(forum_id):
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT COUNT(*) FROM forum_topics WHERE forum_id = %s AND deleted = 0",
            [forum_id],
        )
        return cursor.fetchone()[0]


def get_topics(forum_id, offset, limit):
    views_sel = "t.views," if has_views_column() else "0 AS views,"
    with _conn().cursor() as cursor:
        cursor.execute(
            f"""
            SELECT t.id, t.name, t.poster, t.poster_id, t.created, t.sticky,
                   t.locked, t.moved, t.updated_at, {views_sel}
                   (SELECT COUNT(*) FROM forum_posts p
                      WHERE p.topic_id = t.id AND p.deleted = 0) AS post_count,
                   (SELECT p2.poster FROM forum_posts p2
                      WHERE p2.topic_id = t.id AND p2.deleted = 0
                      ORDER BY p2.time DESC, p2.id DESC LIMIT 1) AS last_poster,
                   (SELECT p3.time FROM forum_posts p3
                      WHERE p3.topic_id = t.id AND p3.deleted = 0
                      ORDER BY p3.time DESC, p3.id DESC LIMIT 1) AS last_time
            FROM forum_topics t
            WHERE t.forum_id = %s AND t.deleted = 0
            ORDER BY t.sticky DESC, t.updated_at DESC, t.id DESC
            LIMIT %s OFFSET %s
            """,
            [forum_id, limit, offset],
        )
        return _dictfetchall(cursor)


# --------------------------------------------------------------------------
# Tema individual y sus posts (paginado)
# --------------------------------------------------------------------------

def get_topic(topic_id, include_hidden=False):
    views_sel = ", views" if has_views_column() else ", 0 AS views"
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT id, forum_id, name, poster, poster_id, created, sticky, "
            f"locked, deleted, moved, updated_at{views_sel} FROM forum_topics WHERE id = %s",
            [topic_id],
        )
        topic = _dictfetchone(cursor)
    if not topic:
        return None
    if not include_hidden and topic['deleted']:
        return None
    return topic


def count_posts(topic_id, include_deleted=False):
    delf = "" if include_deleted else "AND deleted = 0"
    with _conn().cursor() as cursor:
        cursor.execute(
            f"SELECT COUNT(*) FROM forum_posts WHERE topic_id = %s {delf}",
            [topic_id],
        )
        return cursor.fetchone()[0]


def get_posts(topic_id, offset, limit, include_deleted=False):
    delf = "" if include_deleted else "AND deleted = 0"
    with _conn().cursor() as cursor:
        cursor.execute(
            f"""
            SELECT id, topic_id, poster, poster_id, text, time, deleted, updated_at
            FROM forum_posts
            WHERE topic_id = %s {delf}
            ORDER BY time ASC, id ASC
            LIMIT %s OFFSET %s
            """,
            [topic_id, limit, offset],
        )
        return _dictfetchall(cursor)


def get_post(post_id):
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT id, topic_id, poster, poster_id, text, time, deleted, updated_at "
            "FROM forum_posts WHERE id = %s",
            [post_id],
        )
        return _dictfetchone(cursor)


# --------------------------------------------------------------------------
# Escritura: crear tema, responder, editar, borrar/restaurar
# --------------------------------------------------------------------------

def create_topic(forum_id, name, poster, poster_id, text):
    """Crea un tema y su primer post. Devuelve el id del tema."""
    with _conn().cursor() as cursor:
        cursor.execute(
            "INSERT INTO forum_topics "
            "(forum_id, name, poster, poster_id, created, created_at, updated_at, locked, sticky, deleted) "
            "VALUES (%s, %s, %s, %s, NOW(), NOW(), NOW(), 0, 0, 0)",
            [forum_id, name, poster, poster_id],
        )
        cursor.execute("SELECT LAST_INSERT_ID()")
        topic_id = cursor.fetchone()[0]
        cursor.execute(
            "INSERT INTO forum_posts (topic_id, poster, poster_id, text, time, created_at, deleted) "
            "VALUES (%s, %s, %s, %s, NOW(), NOW(), 0)",
            [topic_id, poster, poster_id, text],
        )
    return topic_id


def create_post(topic_id, poster, poster_id, text):
    """Añade una respuesta y actualiza la marca de tiempo del tema."""
    with _conn().cursor() as cursor:
        cursor.execute(
            "INSERT INTO forum_posts (topic_id, poster, poster_id, text, time, created_at, deleted) "
            "VALUES (%s, %s, %s, %s, NOW(), NOW(), 0)",
            [topic_id, poster, poster_id, text],
        )
        cursor.execute("SELECT LAST_INSERT_ID()")
        post_id = cursor.fetchone()[0]
        cursor.execute(
            "UPDATE forum_topics SET updated_at = NOW() WHERE id = %s", [topic_id]
        )
    return post_id


def get_author_info(poster_id):
    """
    Datos del autor para mostrar en los posts: nivel GM, fecha de registro y
    nº de mensajes. Cruza acore_auth (account/account_access) y acore_web.
    """
    info = {'gmlevel': 0, 'joindate': None, 'post_count': 0, 'status': 'Miembro',
            'display_name': None}
    if not poster_id:
        return info
    info['display_name'] = get_display_name(poster_id)
    try:
        with connections['acore_auth'].cursor() as cursor:
            cursor.execute("SELECT MAX(gmlevel) FROM account_access WHERE id = %s", [poster_id])
            row = cursor.fetchone()
            info['gmlevel'] = int(row[0]) if row and row[0] is not None else 0
            cursor.execute("SELECT joindate FROM account WHERE id = %s", [poster_id])
            row = cursor.fetchone()
            if row:
                info['joindate'] = row[0]
    except Exception:
        pass
    try:
        with _conn().cursor() as cursor:
            cursor.execute(
                "SELECT COUNT(*) FROM forum_posts WHERE poster_id = %s AND deleted = 0",
                [poster_id],
            )
            info['post_count'] = cursor.fetchone()[0]
    except Exception:
        pass
    if info['gmlevel'] >= 16:
        info['status'] = 'Administrador'
    elif info['gmlevel'] >= 1:
        info['status'] = 'GM'
    return info


def update_post(post_id, text):
    with _conn().cursor() as cursor:
        cursor.execute(
            "UPDATE forum_posts SET text = %s, updated_at = NOW() WHERE id = %s",
            [text, post_id],
        )


def set_post_deleted(post_id, deleted):
    with _conn().cursor() as cursor:
        cursor.execute(
            "UPDATE forum_posts SET deleted = %s WHERE id = %s",
            [1 if deleted else 0, post_id],
        )


def set_topic_flag(topic_id, field, value):
    """field ∈ {sticky, locked, deleted, moved}."""
    if field not in ('sticky', 'locked', 'deleted', 'moved'):
        raise ValueError('campo no permitido')
    with _conn().cursor() as cursor:
        cursor.execute(
            f"UPDATE forum_topics SET {field} = %s WHERE id = %s",
            [1 if value else 0, topic_id],
        )


def move_topic(topic_id, new_forum_id):
    with _conn().cursor() as cursor:
        cursor.execute(
            "UPDATE forum_topics SET forum_id = %s, moved = 1 WHERE id = %s",
            [new_forum_id, topic_id],
        )


def mark_read(user_id, topic_id):
    if not user_id:
        return
    with _conn().cursor() as cursor:
        cursor.execute(
            "INSERT INTO forum_reads (user_id, topic_id, read_at) "
            "VALUES (%s, %s, NOW()) "
            "ON DUPLICATE KEY UPDATE read_at = NOW()",
            [user_id, topic_id],
        )


def get_display_name(poster_id):
    """
    Nombre legible del autor a partir de su cuenta: parte local del email
    (antes de la @). Si no hay email, devuelve None (la plantilla usa el poster).
    """
    if not poster_id:
        return None
    try:
        with connections['acore_auth'].cursor() as cursor:
            cursor.execute(
                "SELECT email, reg_mail FROM account WHERE id = %s", [poster_id]
            )
            row = cursor.fetchone()
    except Exception:
        return None
    if not row:
        return None
    email = (row[0] or row[1] or '').strip()
    if '@' in email:
        return email.split('@', 1)[0]
    return None


def count_search_topics(query):
    like = f"%{query}%"
    with _conn().cursor() as cursor:
        cursor.execute(
            """
            SELECT COUNT(DISTINCT t.id)
            FROM forum_topics t
            JOIN forums f ON f.id = t.forum_id AND f.visibility = 1 AND f.deleted = 0
            LEFT JOIN forum_posts p ON p.topic_id = t.id AND p.deleted = 0
            WHERE t.deleted = 0 AND (t.name LIKE %s OR p.text LIKE %s)
            """,
            [like, like],
        )
        return cursor.fetchone()[0]


def search_topics(query, offset=0, limit=20):
    """Busca temas por título o por contenido de sus posts (foros visibles)."""
    like = f"%{query}%"
    with _conn().cursor() as cursor:
        cursor.execute(
            """
            SELECT DISTINCT t.id, t.name, t.poster, t.poster_id, t.forum_id,
                   t.created, t.updated_at, f.name AS forum_name
            FROM forum_topics t
            JOIN forums f ON f.id = t.forum_id AND f.visibility = 1 AND f.deleted = 0
            LEFT JOIN forum_posts p ON p.topic_id = t.id AND p.deleted = 0
            WHERE t.deleted = 0 AND (t.name LIKE %s OR p.text LIKE %s)
            ORDER BY t.updated_at DESC
            LIMIT %s OFFSET %s
            """,
            [like, like, limit, offset],
        )
        return _dictfetchall(cursor)


def list_forums_for_move(exclude_forum_id=None):
    """Lista de foros visibles para el desplegable de 'mover tema'."""
    with _conn().cursor() as cursor:
        cursor.execute(
            "SELECT id, name FROM forums WHERE visibility = 1 AND deleted = 0 "
            "ORDER BY `order`, id"
        )
        forums = _dictfetchall(cursor)
    if exclude_forum_id:
        forums = [f for f in forums if f['id'] != exclude_forum_id]
    return forums
