"""
Utility functions for django-lark.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Tuple

import lark

from .client import get_async_lark_client, get_lark_client
from .conf import get_settings

if TYPE_CHECKING:
    from django.contrib.auth.models import AbstractUser


def get_external_id_for_user(user: AbstractUser) -> str:
    """
    Get the Lark external_id for a Django user.

    The external_id is computed based on the LARK_USER_SUBJECT_FIELD setting.
    This can be passed directly to Lark API methods that accept subject_id,
    as Lark's API accepts external_id anywhere a subject_id is expected.

    Args:
        user: Django user instance.

    Returns:
        The external_id string for the user.
    """
    settings = get_settings()
    return _get_user_external_id(user, settings)


def get_or_create_subject_for_user(
    user: AbstractUser,
    *,
    create_if_missing: bool = True,
    update_if_exists: bool = False,
) -> Tuple[lark.types.SubjectResource, bool]:
    """
    Get or create a Lark subject for a Django user.

    Uses the user's external_id to look up or create the subject in Lark.
    No local database records are created.

    Args:
        user: Django user instance.
        create_if_missing: If True, create subject in Lark if not found.
        update_if_exists: If True, update Lark subject with user data.

    Returns:
        Tuple of (SubjectResource, created: bool)

    Raises:
        lark.APIError: If API call fails.
        lark.NotFoundError: If subject not found and create_if_missing=False.
    """
    client = get_lark_client()
    external_id = get_external_id_for_user(user)
    created = False

    # Try to retrieve existing subject by external_id
    try:
        subject = client.subjects.retrieve(external_id)

        if update_if_exists:
            subject = client.subjects.update(
                external_id,
                name=_get_user_name(user),
                email=getattr(user, "email", None),
            )

        return subject, created

    except lark.NotFoundError:
        pass

    # Create new subject if allowed
    if create_if_missing:
        subject = client.subjects.create(
            name=_get_user_name(user),
            email=getattr(user, "email", None),
            external_id=external_id,
        )
        created = True
        return subject, created

    # Re-raise the original NotFoundError if create_if_missing is False
    raise LookupError(f"No Lark subject found for user {user}")


async def aget_or_create_subject_for_user(
    user: AbstractUser,
    *,
    create_if_missing: bool = True,
    update_if_exists: bool = False,
) -> Tuple[lark.types.SubjectResource, bool]:
    """Async version of get_or_create_subject_for_user."""
    client = get_async_lark_client()
    external_id = get_external_id_for_user(user)
    created = False

    # Try to retrieve existing subject by external_id
    try:
        subject = await client.subjects.retrieve(external_id)

        if update_if_exists:
            subject = await client.subjects.update(
                external_id,
                name=_get_user_name(user),
                email=getattr(user, "email", None),
            )

        return subject, created

    except lark.NotFoundError:
        pass

    # Create new subject if allowed
    if create_if_missing:
        subject = await client.subjects.create(
            name=_get_user_name(user),
            email=getattr(user, "email", None),
            external_id=external_id,
        )
        created = True
        return subject, created

    raise LookupError(f"No Lark subject found for user {user}")


def get_billing_state_for_user(user: AbstractUser):
    """
    Get billing state for a Django user.

    Uses the user's external_id to look up their billing state in Lark.

    Args:
        user: Django user instance.

    Returns:
        BillingState from Lark API.

    Raises:
        lark.APIError: If the API call fails.
    """
    external_id = get_external_id_for_user(user)
    client = get_lark_client()
    return client.customer_access.retrieve_billing_state(external_id)


async def aget_billing_state_for_user(user: AbstractUser):
    """Async version of get_billing_state_for_user."""
    external_id = get_external_id_for_user(user)
    client = get_async_lark_client()
    return await client.customer_access.retrieve_billing_state(external_id)


def get_subscriptions_for_user(user: AbstractUser):
    """
    Get subscriptions for a Django user.

    Uses the user's external_id to look up their subscriptions in Lark.

    Args:
        user: Django user instance.

    Returns:
        List of subscriptions from Lark API.

    Raises:
        lark.APIError: If the API call fails.
    """
    external_id = get_external_id_for_user(user)
    client = get_lark_client()
    return client.subscriptions.list(subject_id=external_id)


async def aget_subscriptions_for_user(user: AbstractUser):
    """Async version of get_subscriptions_for_user."""
    external_id = get_external_id_for_user(user)
    client = get_async_lark_client()
    return await client.subscriptions.list(subject_id=external_id)


def _get_user_name(user: AbstractUser) -> str:
    """Get display name for user."""
    if hasattr(user, "get_full_name") and user.get_full_name():
        return user.get_full_name()
    return str(user)


def _get_user_external_id(user: AbstractUser, settings) -> str:
    """Get external_id value based on settings."""
    field = settings.user_subject_field

    if callable(field):
        return field(user)

    if field == "id":
        return f"django_user_{user.pk}"

    return str(getattr(user, field, user.pk))
