import logging

import requests
from django.apps import apps
from django.utils import timezone

from ..utils import get_shop_model, get_auth_shop

logger = logging.getLogger(__name__)

OFFLINE_ACCESS_TOKEN = "urn:shopify:params:oauth:token-type:offline-access-token"
ONLINE_ACCESS_TOKEN = "urn:shopify:params:oauth:token-type:online-access-token"


def exchange_token(
    shop_domain, session_token, requested_token_type=OFFLINE_ACCESS_TOKEN
):
    """Exchange a session token (JWT) for an access token via Shopify's token exchange API."""
    auth_shop = get_auth_shop(shop_domain)

    response = requests.post(
        f"https://{shop_domain}/admin/oauth/access_token",
        data={
            "client_id": auth_shop.shopify_app_api_key,
            "client_secret": auth_shop.shopify_app_api_secret,
            "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
            "subject_token": session_token,
            "subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
            "requested_token_type": requested_token_type,
        },
        headers={
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json",
        },
    )

    if response.status_code != 200:
        logger.error(
            "Token exchange failed for %s: %s %s",
            shop_domain,
            response.status_code,
            response.text,
        )
        return None

    return response.json()


def ensure_access_token(shop_domain, session_token, request=None):
    """Ensure the shop has a valid offline access token, exchanging if needed.

    Returns the shop instance, or None if token exchange failed.
    """

    Shop = get_shop_model()
    shop, _ = Shop.objects.get_or_create(shopify_domain=shop_domain)

    if shop.shopify_token:
        return shop

    logger.info("No access token for %s, performing token exchange", shop_domain)

    data = exchange_token(shop_domain, session_token, OFFLINE_ACCESS_TOKEN)

    if not data:
        return None

    installed = bool(not shop.shopify_token and data["access_token"])

    shop.shopify_token = data["access_token"]
    shop.access_scopes = data.get("scope", "")[:249]
    shop.save()

    if installed:
        shop.installed(request=request)

    return shop


def _should_fetch_user_details(shop, user_id):
    """Check if we need to fetch full user details (new user or last login > 24h)."""
    user_data = shop.seen_users.get(user_id)
    if not user_data:
        return True

    last_login = user_data.get("last_login")
    if not last_login:
        return True

    last_login_dt = timezone.datetime.fromisoformat(last_login)
    if timezone.is_naive(last_login_dt):
        last_login_dt = timezone.make_aware(last_login_dt)

    return (timezone.now() - last_login_dt).total_seconds() > 86400


def track_user_login(shop, user_id, session_token):
    """Track user login and fire on_user_login if new user or stale (>24h)."""
    user_id = str(user_id)

    if not _should_fetch_user_details(shop, user_id):
        return False

    data = exchange_token(shop.shopify_domain, session_token, ONLINE_ACCESS_TOKEN)

    associated_user = (
        data.get("associated_user", {"id": user_id}) if data else {"id": user_id}
    )

    shop.seen_users[user_id] = {"last_login": timezone.now().isoformat()}
    shop.save(update_fields=["seen_users"])
    shop.on_user_login(associated_user)
    return True
