import html
import re
import secrets
import time
import logging
from datetime import datetime, timedelta, timezone
from passlib.hash import pbkdf2_sha256
from fastapi import Request, HTTPException, status
from devplacepy.database import get_table
from devplacepy.config import SECRET_KEY, SESSION_MAX_AGE

logger = logging.getLogger(__name__)


def hash_password(password: str) -> str:
    return pbkdf2_sha256.hash(password)


def verify_password(password: str, hashed: str) -> bool:
    return pbkdf2_sha256.verify(password, hashed)


def create_session(user_uid: str) -> str:
    token = secrets.token_hex(32)
    expires_at = datetime.now(timezone.utc) + timedelta(seconds=SESSION_MAX_AGE)
    sessions = get_table("sessions")
    sessions.insert({
        "session_token": token,
        "user_uid": user_uid,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "expires_at": expires_at.isoformat(),
    })
    return token


_user_cache = {}
_USER_CACHE_TTL = 300


def get_current_user(request: Request):
    token = request.cookies.get("session")
    if not token:
        return None

    cached = _user_cache.get(token)
    if cached:
        entry, expiry = cached
        if time.time() < expiry:
            return entry
        _user_cache.pop(token, None)

    sessions = get_table("sessions")
    session = sessions.find_one(session_token=token)
    if not session:
        return None
    expires = datetime.fromisoformat(session["expires_at"])
    if expires.tzinfo is None:
        expires = expires.replace(tzinfo=timezone.utc)
    if expires < datetime.now(timezone.utc):
        sessions.delete(id=session["id"])
        return None
    users = get_table("users")
    user = users.find_one(uid=session["user_uid"])
    if user and not user.get("is_active", True):
        sessions.delete(id=session["id"])
        _user_cache.pop(token, None)
        return None
    if user:
        _user_cache[token] = (user, time.time() + _USER_CACHE_TTL)
    return user


def require_user(request: Request):
    user = get_current_user(request)
    if not user:
        raise HTTPException(status_code=status.HTTP_303_SEE_OTHER, headers={"Location": "/"})
    return user


def require_admin(request: Request):
    user = require_user(request)
    if user.get("role") != "Admin":
        raise HTTPException(status_code=status.HTTP_303_SEE_OTHER, headers={"Location": "/feed"})
    return user


def strip_html(text: str) -> str:
    if not text:
        return ""
    text = re.sub(r"<[^>]+>", " ", text)
    text = html.unescape(text)
    return re.sub(r"\s+", " ", text).strip()


def slugify(text: str) -> str:
    text = text.lower().strip()
    text = re.sub(r"[^a-z0-9-]", "-", text)
    text = re.sub(r"-+", "-", text)
    return text.strip("-")


def generate_uid() -> str:
    import uuid
    return str(uuid.uuid4())


def make_combined_slug(text: str, uid: str) -> str:
    short_uid = uid[:8]
    slug_part = slugify(text)
    if not slug_part:
        return short_uid
    return f"{short_uid}-{slug_part}"


def time_ago(dt_str: str) -> str:
    dt = datetime.fromisoformat(dt_str)
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    now = datetime.now(timezone.utc)
    diff = now - dt
    days = diff.days
    if days > 30:
        return dt.strftime("%d/%m/%Y")
    if days > 0:
        return f"{days}d ago"
    hours = diff.seconds // 3600
    if hours > 0:
        return f"{hours}h ago"
    minutes = diff.seconds // 60
    if minutes > 0:
        return f"{minutes}m ago"
    return "just now"


def extract_mentions(content: str) -> list[str]:
    if not content:
        return []
    return re.findall(r"(?:^|[\s(])@([a-zA-Z0-9_-]+)", content)


def create_mention_notifications(content: str, actor_uid: str, target_url: str) -> None:
    usernames = extract_mentions(content)
    if not usernames:
        return
    from devplacepy.templating import clear_unread_cache
    users = get_table("users")
    notifs = get_table("notifications")
    seen = set()
    for username in usernames:
        if username in seen:
            continue
        seen.add(username)
        mentioned = users.find_one(username=username)
        if mentioned and mentioned["uid"] != actor_uid:
            notifs.insert({
                "uid": generate_uid(),
                "user_uid": mentioned["uid"],
                "type": "mention",
                "message": f"@{username} mentioned you",
                "related_uid": actor_uid,
                "target_url": target_url,
                "read": False,
                "created_at": datetime.now(timezone.utc).isoformat(),
            })
            clear_unread_cache(mentioned["uid"])


def format_date(dt_str: str, include_time: bool = False) -> str:
    if not dt_str:
        return ""
    try:
        dt = datetime.fromisoformat(dt_str)
        if include_time:
            return dt.strftime("%d/%m/%Y %H:%M")
        return dt.strftime("%d/%m/%Y")
    except (ValueError, TypeError):
        return dt_str
