import dataset
import logging
from datetime import datetime
from devplacepy.config import DATABASE_URL

logger = logging.getLogger(__name__)

db = dataset.connect(
    DATABASE_URL,
    engine_kwargs={
        "connect_args": {
            "timeout": 30,
            "check_same_thread": False,
        },
    },
    on_connect_statements=[
        "PRAGMA journal_mode=WAL",
        "PRAGMA synchronous=NORMAL",
        "PRAGMA busy_timeout=30000",
        "PRAGMA cache_size=-8000",
        "PRAGMA temp_store=MEMORY",
        "PRAGMA mmap_size=268435456",
    ],
)


def _index(db, table, name, columns):
    try:
        if table in db.tables:
            cols = ", ".join(columns)
            db.query(f"CREATE INDEX IF NOT EXISTS {name} ON {table} ({cols})")
    except Exception as e:
        logger.warning(f"Could not create index {name} on {table}: {e}")


def init_db():
    tables = db.tables
    _index(db, "users", "idx_users_username", ["username"])
    _index(db, "users", "idx_users_email", ["email"])
    _index(db, "posts", "idx_posts_user_uid", ["user_uid"])
    _index(db, "posts", "idx_posts_created_at", ["created_at"])
    _index(db, "posts", "idx_posts_topic", ["topic"])
    _index(db, "comments", "idx_comments_post_uid", ["post_uid"])
    _index(db, "comments", "idx_comments_target", ["target_type", "target_uid"])
    _index(db, "comments", "idx_comments_user_uid", ["user_uid"])
    _index(db, "comments", "idx_comments_created_at", ["created_at"])
    _index(db, "votes", "idx_votes_target", ["target_uid", "target_type"])
    _index(db, "messages", "idx_messages_sender", ["sender_uid"])
    _index(db, "messages", "idx_messages_receiver", ["receiver_uid"])
    _index(db, "notifications", "idx_notifications_user", ["user_uid"])
    _index(db, "notifications", "idx_notifications_user_read", ["user_uid", "read"])
    _index(db, "sessions", "idx_sessions_token", ["session_token"])
    _index(db, "projects", "idx_projects_user", ["user_uid"])
    _index(db, "badges", "idx_badges_user", ["user_uid"])
    _index(db, "follows", "idx_follows_follower", ["follower_uid"])
    _index(db, "follows", "idx_follows_following", ["following_uid"])
    _index(db, "password_resets", "idx_password_resets_token", ["token"])
    _index(db, "gists", "idx_gists_user_uid", ["user_uid"])
    _index(db, "attachments", "idx_attachments_resource", ["resource_type", "resource_uid"])
    _index(db, "attachments", "idx_attachments_target", ["target_type", "target_uid"])

    if "site_settings" in tables:
        defaults = {"site_name": "DevPlace", "site_description": "The Developer Social Network", "site_tagline": "Track industry shifts. Discover bold releases. Share what you are building in an open, uncensored environment."}
        for key, value in defaults.items():
            existing = db["site_settings"].find_one(key=key)
            if not existing:
                db["site_settings"].insert({"uid": f"default_{key}", "key": key, "value": value})

    _index(db, "news", "idx_news_external_id", ["external_id"])
    _index(db, "news", "idx_news_synced_at", ["synced_at"])
    _index(db, "news", "idx_news_status", ["status"])
    _index(db, "news_images", "idx_news_images_news_uid", ["news_uid"])
    _index(db, "news_sync", "idx_news_sync_external_id", ["external_id"])

    if "news" in tables:
        for article in db["news"].find(status=None):
            was_featured = article.get("featured", 0)
            db["news"].update({
                "uid": article["uid"],
                "status": "published" if was_featured else "draft",
            }, ["uid"])
        for article in db["news"].find(show_on_landing=None):
            db["news"].update({
                "uid": article["uid"],
                "show_on_landing": 0,
            }, ["uid"])
        for article in db["news"].find(slug=None):
            from devplacepy.utils import make_combined_slug
            slug = make_combined_slug(article.get("title", "") or "news", article["uid"])
            db["news"].update({
                "uid": article["uid"],
                "slug": slug,
            }, ["uid"])

    if "news_sync" in tables:
        for entry in db["news_sync"].find():
            current = entry.get("status", "")
            if current in ("below_threshold", ""):
                db["news_sync"].update({
                    "id": entry["id"],
                    "status": "graded",
                }, ["id"])

    if "site_settings" in tables:
        news_defaults = {
            "news_grade_threshold": "7",
            "news_api_url": "https://news.app.molodetz.nl/api",
            "news_ai_url": "https://openai.app.molodetz.nl/v1/chat/completions",
            "news_ai_model": "molodetz",
        }
        for key, value in news_defaults.items():
            existing = db["site_settings"].find_one(key=key)
            if not existing:
                db["site_settings"].insert({"uid": f"default_{key}", "key": key, "value": value})

        upload_defaults = {
            "max_upload_size_mb": "10",
            "allowed_file_types": "",
            "max_attachments_per_resource": "10",
        }
        for key, value in upload_defaults.items():
            existing = db["site_settings"].find_one(key=key)
            if not existing:
                db["site_settings"].insert({"uid": f"default_{key}", "key": key, "value": value})

    logger.info("Database initialized")


def get_table(name):
    return db[name]


def get_users_by_uids(uids):
    if not uids:
        return {}
    seen = set()
    unique = [u for u in uids if u not in seen and not seen.add(u)]
    return {u["uid"]: u for u in db["users"].find(db["users"].table.columns.uid.in_(unique))}


def get_comment_counts_by_post_uids(post_uids):
    if not post_uids or "comments" not in db.tables:
        return {}
    placeholders = ", ".join(f":p{i}" for i in range(len(post_uids)))
    params = {f"p{i}": u for i, u in enumerate(post_uids)}
    rows = db.query(f"SELECT target_uid, COUNT(*) as c FROM comments WHERE target_type='post' AND target_uid IN ({placeholders}) GROUP BY target_uid", **params)
    return {r["target_uid"]: r["c"] for r in rows}


def get_vote_counts(target_uids):
    if not target_uids or "votes" not in db.tables:
        return {}, {}
    placeholders = ", ".join(f":p{i}" for i in range(len(target_uids)))
    params = {f"p{i}": u for i, u in enumerate(target_uids)}
    rows = db.query(f"SELECT target_uid, value, COUNT(*) as c FROM votes WHERE target_uid IN ({placeholders}) GROUP BY target_uid, value", **params)
    ups = {}
    downs = {}
    for r in rows:
        if r["value"] == 1:
            ups[r["target_uid"]] = r["c"]
        else:
            downs[r["target_uid"]] = r["c"]
    return ups, downs


def load_comments(target_type, target_uid):
    if "comments" not in db.tables:
        return []
    comments_table = db["comments"]
    raw = list(comments_table.find(target_type=target_type, target_uid=target_uid, order_by=["created_at"]))
    if not raw and target_type == "post":
        raw = list(comments_table.find(post_uid=target_uid, order_by=["created_at"]))
    if not raw:
        return []
    uids = [c["user_uid"] for c in raw]
    cids = [c["uid"] for c in raw]
    users = get_users_by_uids(uids)
    ups, downs = get_vote_counts(cids)
    from devplacepy.utils import time_ago
    from devplacepy.attachments import get_attachments_batch as _gab
    atts_map = _gab("comment", cids) if "attachments" in db.tables else {}
    cmap = {}
    for c in raw:
        cmap[c["uid"]] = {
            "comment": c,
            "author": users.get(c["user_uid"]),
            "time_ago": time_ago(c["created_at"]),
            "votes": {"up": ups.get(c["uid"], 0), "down": downs.get(c["uid"], 0)},
            "children": [],
            "attachments": atts_map.get(c["uid"], []),
        }
    top = []
    for item in cmap.values():
        parent = item["comment"].get("parent_uid")
        if parent and parent in cmap:
            cmap[parent]["children"].append(item)
        else:
            top.append(item)
    return top


def get_attachments(resource_type: str, resource_uid: str) -> list:
    if "attachments" not in db.tables:
        return []
    return list(db["attachments"].find(resource_type=resource_type, resource_uid=resource_uid, order_by=["created_at"]))


def get_attachments_by_type(resource_type: str, resource_uids: list) -> dict:
    if not resource_uids or "attachments" not in db.tables:
        return {}
    rows = list(db["attachments"].find(db["attachments"].table.columns.resource_uid.in_(resource_uids), resource_type=resource_type))
    result = {}
    for a in rows:
        key = a["resource_uid"]
        if key not in result:
            result[key] = []
        result[key].append(a)
    return result


def delete_attachment_record(uid: str) -> None:
    if "attachments" not in db.tables:
        return
    att = db["attachments"].find_one(uid=uid)
    if att:
        _delete_attachment_file(att.get("storage_path", ""))
        db["attachments"].delete(id=att["id"])


def delete_attachments(resource_type: str, resource_uid: str) -> None:
    if "attachments" not in db.tables:
        return
    for a in db["attachments"].find(resource_type=resource_type, resource_uid=resource_uid):
        _delete_attachment_file(a.get("storage_path", ""))
    db["attachments"].delete(resource_type=resource_type, resource_uid=resource_uid)


def _delete_attachment_file(storage_path: str) -> None:
    if not storage_path:
        return
    from devplacepy.config import STATIC_DIR
    file_path = STATIC_DIR / "uploads" / storage_path
    try:
        file_path.unlink(missing_ok=True)
        parent = file_path.parent
        if parent.exists() and not any(parent.iterdir()):
            parent.rmdir()
        grandparent = parent.parent
        if grandparent.exists() and not any(grandparent.iterdir()):
            grandparent.rmdir()
    except Exception as e:
        logger.warning(f"Failed to delete attachment file {storage_path}: {e}")


def get_setting(key: str, default: str = "") -> str:
    if "site_settings" not in db.tables:
        return default
    entry = db["site_settings"].find_one(key=key)
    return entry["value"] if entry else default


def resolve_by_slug(table, slug):
    entry = table.find_one(slug=slug)
    if not entry:
        entry = table.find_one(uid=slug)
    return entry


def build_pagination(page, total, per_page=25):
    total_pages = max(1, __import__("math").ceil(total / per_page))
    page = max(1, min(page, total_pages))
    return {
        "page": page,
        "per_page": per_page,
        "total": total,
        "total_pages": total_pages,
        "has_prev": page > 1,
        "has_next": page < total_pages,
        "prev_page": page - 1,
        "next_page": page + 1,
    }


def get_daily_topic():
    if "news" in db.tables:
        article = db["news"].find_one(status="published", order_by=["-synced_at"])
        if article:
            desc = (article.get("description") or "")[:200] or (article.get("content") or "")[:200]
            return {
                "title": article.get("title", ""),
                "summary": desc,
                "slug": article.get("slug", ""),
                "url": article.get("url", ""),
            }
    return {"title": "Welcome to DevPlace", "summary": "Stay tuned for the latest dev news."}
