import datetime as dt
from functools import wraps

import jwt
from flask import current_app, g, request

from db import query_one
from utils import looks_like_phone, normalize_phone, today_local, whatsapp_url

OPEN_STATES = ("active", "grace")


class ApiError(Exception):
    def __init__(self, status, code, message, **extra):
        super().__init__(message)
        self.status, self.code, self.message, self.extra = status, code, message, extra


# ---------------- التوكن ----------------
def make_token(user) -> str:
    now = dt.datetime.now(dt.timezone.utc)
    payload = {
        "sub": str(user["id"]),
        "iat": now,
        "exp": now + dt.timedelta(days=current_app.config["JWT_DAYS"]),
    }
    return jwt.encode(payload, current_app.config["JWT_SECRET"], algorithm="HS256")


def _load_user():
    header = request.headers.get("Authorization", "")
    if not header.startswith("Bearer "):
        return None
    try:
        data = jwt.decode(header[7:], current_app.config["JWT_SECRET"], algorithms=["HS256"])
        uid = int(data["sub"])
    except (jwt.PyJWTError, KeyError, ValueError):
        return None
    user = query_one("SELECT * FROM users WHERE id=%s", (uid,))
    if not user or user["is_blocked"]:
        return None
    return user


# ---------------- الاشتراك ----------------
def subscription_state(captain) -> str:
    """active | grace | expired | inactive"""
    if not captain or not captain["is_active"] or not captain["paid_until"]:
        return "inactive"
    today = today_local()
    paid_until = captain["paid_until"]
    if today <= paid_until:
        return "active"
    if today <= paid_until + dt.timedelta(days=current_app.config["GRACE_DAYS"]):
        return "grace"
    return "expired"


def login_required(*roles, subscription=False):
    """roles: الأدوار المسموحة. subscription=True: يقفل الكابتن غير المفعّل."""

    def deco(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            user = _load_user()
            if not user:
                raise ApiError(401, "unauthorized", "يجب تسجيل الدخول")
            if roles and user["role"] not in roles:
                raise ApiError(403, "forbidden", "غير مسموح")
            g.user = user
            if user["role"] == "captain":
                g.captain = query_one("SELECT * FROM captains WHERE user_id=%s", (user["id"],))
                if subscription:
                    state = subscription_state(g.captain)
                    if state not in OPEN_STATES:
                        # رسالة محايدة (متطلبات متجر Apple: لا نذكر الدفع داخل التطبيق)
                        raise ApiError(403, "subscription_locked",
                                       "الحساب غير مفعّل، تواصل مع الإدارة", state=state)
            return fn(*args, **kwargs)

        return wrapper

    return deco


# ---------------- مساعدات ----------------
def find_user_by_identifier(ident):
    """الدخول باليوزر أو رقم الهاتف."""
    ident = (ident or "").strip()
    if not ident:
        return None
    if looks_like_phone(ident):
        try:
            phone = normalize_phone(ident)
        except ValueError:
            return None
        return query_one("SELECT * FROM users WHERE phone=%s", (phone,))
    return query_one("SELECT * FROM users WHERE username=%s", (ident.lower(),))


def public_user(u):
    return {
        "id": u["id"],
        "role": u["role"],
        "full_name": u["full_name"],
        "username": u["username"],
        "phone": u["phone"],
        "whatsapp_url": whatsapp_url(u["phone"]),
        "avatar_path": u["avatar_path"],
    }
