from __future__ import annotations

import lark_oapi as lark
from lark_oapi.api.contact.v3 import (
    BatchGetIdUserRequest,
    BatchGetIdUserRequestBody,
    GetUserRequest,
    ListUserRequest,
    GetDepartmentRequest,
    ListDepartmentRequest,
)

from ._client import get_client, make_option
from ._scopes import InsufficientScopeError, MODULE_SCOPES


def get_user(token: str, user_id: str, *, user_id_type: str = "open_id") -> object:
    client = get_client()
    request = (
        GetUserRequest.builder()
        .user_id(user_id)
        .user_id_type(user_id_type)
        .build()
    )
    response = client.contact.v3.user.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
        raise RuntimeError(
            f"获取用户信息失败: code={response.code}, msg={response.msg}"
        )
    return response.data.user


def batch_get_user_ids(
    token: str,
    emails: list[str] | None = None,
    mobiles: list[str] | None = None,
) -> object:
    client = get_client()
    body_builder = BatchGetIdUserRequestBody.builder()
    if emails:
        body_builder = body_builder.emails(emails)
    if mobiles:
        body_builder = body_builder.mobiles(mobiles)
    request = (
        BatchGetIdUserRequest.builder()
        .request_body(body_builder.build())
        .build()
    )
    response = client.contact.v3.user.batch_get_id(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
        raise RuntimeError(
            f"批量获取用户 ID 失败: code={response.code}, msg={response.msg}"
        )
    return response.data


def list_users_in_department(
    token: str,
    department_id: str,
    *,
    department_id_type: str = "department_id",
    user_id_type: str = "open_id",
    page_size: int = 50,
) -> list:
    client = get_client()
    all_items: list = []
    page_token: str | None = None
    while True:
        builder = (
            ListUserRequest.builder()
            .department_id(department_id)
            .department_id_type(department_id_type)
            .user_id_type(user_id_type)
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.contact.v3.user.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
            raise RuntimeError(
                f"获取部门成员失败: code={response.code}, msg={response.msg}"
            )
        all_items.extend(response.data.items or [])
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items


def get_department(
    token: str,
    department_id: str,
    *,
    department_id_type: str = "department_id",
) -> object:
    client = get_client()
    request = (
        GetDepartmentRequest.builder()
        .department_id(department_id)
        .department_id_type(department_id_type)
        .build()
    )
    response = client.contact.v3.department.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
        raise RuntimeError(
            f"获取部门信息失败: code={response.code}, msg={response.msg}"
        )
    return response.data.department


def list_departments(
    token: str,
    *,
    parent_department_id: str = "0",
    department_id_type: str = "department_id",
    fetch_child: bool = False,
    page_size: int = 50,
) -> list:
    client = get_client()
    all_items: list = []
    page_token: str | None = None
    while True:
        builder = (
            ListDepartmentRequest.builder()
            .parent_department_id(parent_department_id)
            .department_id_type(department_id_type)
            .fetch_child(fetch_child)
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.contact.v3.department.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
            raise RuntimeError(
                f"获取子部门列表失败: code={response.code}, msg={response.msg}"
            )
        all_items.extend(response.data.items or [])
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items


def search_user(
    token: str,
    query: str,
    *,
    page_size: int = 20,
    max_count: int = 50,
) -> list:
    client = get_client()
    all_items: list = []
    page_token: str | None = None

    while len(all_items) < max_count:
        queries = [("query", query), ("page_size", str(min(page_size, 50)))]
        if page_token:
            queries.append(("page_token", page_token))

        request = (
            lark.BaseRequest.builder()
            .http_method(lark.HttpMethod.GET)
            .uri("/open-apis/search/v1/user")
            .token_types({lark.AccessTokenType.USER})
            .queries(queries)
            .build()
        )
        response = client.request(request, make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("contact", []), module="contact")
            raise RuntimeError(
                f"搜索用户失败: code={response.code}, msg={response.msg}"
            )
        import json as _json
        data = _json.loads(response.raw.content)
        items = (data.get("data") or {}).get("users") or []
        all_items.extend(items)
        has_more = (data.get("data") or {}).get("has_more", False)
        if not has_more:
            break
        page_token = (data.get("data") or {}).get("page_token")

    return all_items[:max_count]


def list_user_departments(
    token: str,
    user_id: str,
    *,
    user_id_type: str = "open_id",
    department_id_type: str = "department_id",
) -> list:
    user = get_user(token, user_id, user_id_type=user_id_type)
    return list(getattr(user, "department_ids", None) or [])
