"""User review DB operations."""

from __future__ import annotations

from datetime import date

import oracledb

from core.db import conn


def create_review(
    user_id: str,
    restaurant_id: str,
    rating: float,
    review_text: str | None = None,
    visited_at: date | None = None,
) -> dict:
    """Create a new review. Returns the created review dict."""
    sql = """
        INSERT INTO user_reviews (user_id, restaurant_id, rating, review_text, visited_at)
        VALUES (:user_id, :restaurant_id, :rating, :review_text, :visited_at)
        RETURNING id INTO :out_id
    """
    with conn() as c:
        cur = c.cursor()
        out_id = cur.var(oracledb.STRING)
        cur.execute(sql, {
            "user_id": user_id,
            "restaurant_id": restaurant_id,
            "rating": rating,
            "review_text": review_text,
            "visited_at": visited_at,
            "out_id": out_id,
        })
        new_id = out_id.getvalue()[0]

    return get_review_by_id(new_id)


def update_review(
    review_id: str,
    user_id: str,
    rating: float | None = None,
    review_text: str | None = None,
    visited_at: date | None = None,
) -> dict:
    """Update an existing review. Only the owner can update.

    Returns the updated review dict, or None if not found / not owner.
    """
    sql = """
        UPDATE user_reviews
        SET rating = COALESCE(:rating, rating),
            review_text = COALESCE(:review_text, review_text),
            visited_at = COALESCE(:visited_at, visited_at),
            updated_at = SYSTIMESTAMP
        WHERE id = :id AND user_id = :user_id
    """
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {
            "rating": rating,
            "review_text": review_text,
            "visited_at": visited_at,
            "id": review_id,
            "user_id": user_id,
        })
        if cur.rowcount == 0:
            return None

    return get_review_by_id(review_id)


def delete_review(review_id: str, user_id: str) -> bool:
    """Delete a review. Only the owner can delete. Returns True if deleted."""
    sql = "DELETE FROM user_reviews WHERE id = :id AND user_id = :user_id"
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {"id": review_id, "user_id": user_id})
        return cur.rowcount > 0


def get_review_by_id(review_id: str) -> dict | None:
    """Get a single review by ID."""
    sql = """
        SELECT r.id, r.user_id, r.restaurant_id, r.rating, r.review_text,
               r.visited_at, r.created_at, r.updated_at,
               u.nickname, u.avatar_url
        FROM user_reviews r
        JOIN tasteby_users u ON u.id = r.user_id
        WHERE r.id = :id
    """
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {"id": review_id})
        row = cur.fetchone()
        if not row:
            return None
        return _row_to_dict(row)


def get_reviews_for_restaurant(
    restaurant_id: str,
    limit: int = 20,
    offset: int = 0,
) -> list[dict]:
    """List reviews for a restaurant, including user nickname/avatar."""
    sql = """
        SELECT r.id, r.user_id, r.restaurant_id, r.rating, r.review_text,
               r.visited_at, r.created_at, r.updated_at,
               u.nickname, u.avatar_url
        FROM user_reviews r
        JOIN tasteby_users u ON u.id = r.user_id
        WHERE r.restaurant_id = :restaurant_id
        ORDER BY r.created_at DESC
        OFFSET :off ROWS FETCH NEXT :lim ROWS ONLY
    """
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {
            "restaurant_id": restaurant_id,
            "off": offset,
            "lim": limit,
        })
        return [_row_to_dict(row) for row in cur.fetchall()]


def get_user_reviews(
    user_id: str,
    limit: int = 20,
    offset: int = 0,
) -> list[dict]:
    """List reviews by a specific user, including restaurant name."""
    sql = """
        SELECT r.id, r.user_id, r.restaurant_id, r.rating, r.review_text,
               r.visited_at, r.created_at, r.updated_at,
               u.nickname, u.avatar_url,
               rest.name AS restaurant_name
        FROM user_reviews r
        JOIN tasteby_users u ON u.id = r.user_id
        LEFT JOIN restaurants rest ON rest.id = r.restaurant_id
        WHERE r.user_id = :user_id
        ORDER BY r.created_at DESC
        OFFSET :off ROWS FETCH NEXT :lim ROWS ONLY
    """
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {
            "user_id": user_id,
            "off": offset,
            "lim": limit,
        })
        rows = []
        for row in cur.fetchall():
            d = _row_to_dict(row)
            d["restaurant_name"] = row[10]
            rows.append(d)
        return rows


def get_restaurant_avg_rating(restaurant_id: str) -> dict:
    """Get average rating and review count for a restaurant."""
    sql = """
        SELECT ROUND(AVG(rating), 1) AS avg_rating, COUNT(*) AS review_count
        FROM user_reviews
        WHERE restaurant_id = :restaurant_id
    """
    with conn() as c:
        cur = c.cursor()
        cur.execute(sql, {"restaurant_id": restaurant_id})
        row = cur.fetchone()
        return {
            "avg_rating": float(row[0]) if row[0] else None,
            "review_count": int(row[1]),
        }


def _row_to_dict(row) -> dict:
    """Convert a review query row to a dict."""
    review_text = row[4]
    if hasattr(review_text, "read"):
        review_text = review_text.read()

    return {
        "id": row[0],
        "user_id": row[1],
        "restaurant_id": row[2],
        "rating": float(row[3]),
        "review_text": review_text,
        "visited_at": row[5].isoformat() if row[5] else None,
        "created_at": row[6].isoformat() if row[6] else None,
        "updated_at": row[7].isoformat() if row[7] else None,
        "user_nickname": row[8],
        "user_avatar_url": row[9],
    }
