from __future__ import annotations

from lark_oapi.api.bitable.v1 import (
    AppTableField,
    AppTableRecord,
    BatchCreateAppTableRecordRequest,
    BatchCreateAppTableRecordRequestBody,
    BatchDeleteAppTableRecordRequest,
    BatchDeleteAppTableRecordRequestBody,
    BatchUpdateAppTableRecordRequest,
    BatchUpdateAppTableRecordRequestBody,
    CreateAppRequest,
    CreateAppTableFieldRequest,
    CreateAppTableRecordRequest,
    CreateAppTableRequest,
    CreateAppTableRequestBody,
    CreateAppTableViewRequest,
    DeleteAppTableFieldRequest,
    DeleteAppTableRecordRequest,
    DeleteAppTableRequest,
    DeleteAppTableViewRequest,
    GetAppRequest,
    GetAppTableRecordRequest,
    ListAppTableFieldRequest,
    ListAppTableRecordRequest,
    ListAppTableRequest,
    ListAppTableViewRequest,
    ReqApp,
    ReqTable,
    ReqView,
    UpdateAppTableFieldRequest,
    UpdateAppTableRecordRequest,
)

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


def create_app(token: str, name: str, *, folder_token: str = "") -> object:
    client = get_client()
    body_builder = ReqApp.builder().name(name)
    if folder_token:
        body_builder = body_builder.folder_token(folder_token)
    request = CreateAppRequest.builder().request_body(body_builder.build()).build()
    response = client.bitable.v1.app.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"创建多维表格失败: code={response.code}, msg={response.msg}"
        )
    return response.data.app


def get_app(token: str, app_token: str) -> object:
    client = get_client()
    request = GetAppRequest.builder().app_token(app_token).build()
    response = client.bitable.v1.app.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"获取多维表格失败: code={response.code}, msg={response.msg}"
        )
    return response.data.app


def list_tables(token: str, app_token: str, *, page_size: int = 20) -> list:
    client = get_client()
    all_items: list = []
    page_token: str | None = None
    while True:
        builder = ListAppTableRequest.builder().app_token(app_token).page_size(page_size)
        if page_token:
            builder = builder.page_token(page_token)
        response = client.bitable.v1.app_table.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
            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 create_table(token: str, app_token: str, name: str) -> object:
    client = get_client()
    request = (
        CreateAppTableRequest.builder()
        .app_token(app_token)
        .request_body(
            CreateAppTableRequestBody.builder()
            .table(ReqTable.builder().name(name).build())
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"创建数据表失败: code={response.code}, msg={response.msg}"
        )
    return response.data


def get_record(token: str, app_token: str, table_id: str, record_id: str) -> object:
    client = get_client()
    request = (
        GetAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .record_id(record_id)
        .build()
    )
    response = client.bitable.v1.app_table_record.get(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"获取记录失败: code={response.code}, msg={response.msg}"
        )
    return response.data.record


def list_records(
    token: str,
    app_token: str,
    table_id: str,
    *,
    filter: str = "",
    sort: list[str] | None = None,
    page_size: int = 100,
    max_count: int = 5000,
) -> list:
    client = get_client()
    all_items: list = []
    page_token: str | None = None
    while len(all_items) < max_count:
        builder = (
            ListAppTableRecordRequest.builder()
            .app_token(app_token)
            .table_id(table_id)
            .page_size(page_size)
        )
        if filter:
            builder = builder.filter(filter)
        if page_token:
            builder = builder.page_token(page_token)
        response = client.bitable.v1.app_table_record.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
            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[:max_count]


def create_record(
    token: str,
    app_token: str,
    table_id: str,
    fields: dict,
) -> object:
    client = get_client()
    request = (
        CreateAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(
            AppTableRecord.builder()
            .fields(fields)
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table_record.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"创建记录失败: code={response.code}, msg={response.msg}"
        )
    return response.data.record


def batch_create_records(
    token: str,
    app_token: str,
    table_id: str,
    records: list[dict],
) -> list:
    client = get_client()
    record_objects = [AppTableRecord.builder().fields(r).build() for r in records]
    request = (
        BatchCreateAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(
            BatchCreateAppTableRecordRequestBody.builder()
            .records(record_objects)
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table_record.batch_create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"批量创建记录失败: code={response.code}, msg={response.msg}"
        )
    return response.data.records or []


def update_record(
    token: str,
    app_token: str,
    table_id: str,
    record_id: str,
    fields: dict,
) -> object:
    client = get_client()
    request = (
        UpdateAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .record_id(record_id)
        .request_body(
            AppTableRecord.builder()
            .fields(fields)
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table_record.update(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"更新记录失败: code={response.code}, msg={response.msg}"
        )
    return response.data.record


def batch_update_records(
    token: str,
    app_token: str,
    table_id: str,
    records: list[dict],
) -> list:
    client = get_client()
    record_objects = [
        AppTableRecord.builder().record_id(r["record_id"]).fields(r["fields"]).build()
        for r in records
    ]
    request = (
        BatchUpdateAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(
            BatchUpdateAppTableRecordRequestBody.builder()
            .records(record_objects)
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table_record.batch_update(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"批量更新记录失败: code={response.code}, msg={response.msg}"
        )
    return response.data.records or []


def delete_record(token: str, app_token: str, table_id: str, record_id: str) -> None:
    client = get_client()
    request = (
        DeleteAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .record_id(record_id)
        .build()
    )
    response = client.bitable.v1.app_table_record.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"删除记录失败: code={response.code}, msg={response.msg}"
        )


def batch_delete_records(
    token: str,
    app_token: str,
    table_id: str,
    record_ids: list[str],
) -> None:
    client = get_client()
    request = (
        BatchDeleteAppTableRecordRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(
            BatchDeleteAppTableRecordRequestBody.builder()
            .records(record_ids)
            .build()
        )
        .build()
    )
    response = client.bitable.v1.app_table_record.batch_delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(
            f"批量删除记录失败: code={response.code}, msg={response.msg}"
        )


def list_fields(token: str, app_token: str, table_id: str, *, page_size: int = 50) -> list[dict]:
    client = get_client()
    all_items: list[dict] = []
    page_token: str | None = None
    while True:
        builder = (
            ListAppTableFieldRequest.builder()
            .app_token(app_token)
            .table_id(table_id)
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.bitable.v1.app_table_field.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
            raise RuntimeError(f"获取字段列表失败: code={response.code}, msg={response.msg}")
        for f in response.data.items or []:
            all_items.append({
                "field_id": getattr(f, "field_id", ""),
                "field_name": getattr(f, "field_name", ""),
                "type": getattr(f, "type", 0),
                "ui_type": getattr(f, "ui_type", ""),
                "is_primary": getattr(f, "is_primary", False),
                "is_hidden": getattr(f, "is_hidden", False),
            })
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items


def create_field(
    token: str,
    app_token: str,
    table_id: str,
    field_name: str,
    field_type: int,
    *,
    ui_type: str = "",
    property: dict | None = None,
) -> dict:
    client = get_client()
    field_builder = AppTableField.builder().field_name(field_name).type(field_type)
    if ui_type:
        field_builder = field_builder.ui_type(ui_type)
    if property is not None:
        field_builder = field_builder.property(property)
    request = (
        CreateAppTableFieldRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(field_builder.build())
        .build()
    )
    response = client.bitable.v1.app_table_field.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"创建字段失败: code={response.code}, msg={response.msg}")
    f = response.data.field
    return {
        "field_id": getattr(f, "field_id", ""),
        "field_name": getattr(f, "field_name", ""),
        "type": getattr(f, "type", 0),
        "ui_type": getattr(f, "ui_type", ""),
    }


def update_field(
    token: str,
    app_token: str,
    table_id: str,
    field_id: str,
    *,
    field_name: str | None = None,
    field_type: int | None = None,
    property: dict | None = None,
) -> dict:
    client = get_client()
    field_builder = AppTableField.builder()
    if field_name is not None:
        field_builder = field_builder.field_name(field_name)
    if field_type is not None:
        field_builder = field_builder.type(field_type)
    if property is not None:
        field_builder = field_builder.property(property)
    request = (
        UpdateAppTableFieldRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .field_id(field_id)
        .request_body(field_builder.build())
        .build()
    )
    response = client.bitable.v1.app_table_field.update(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"更新字段失败: code={response.code}, msg={response.msg}")
    f = response.data.field
    return {
        "field_id": getattr(f, "field_id", ""),
        "field_name": getattr(f, "field_name", ""),
        "type": getattr(f, "type", 0),
        "ui_type": getattr(f, "ui_type", ""),
    }


def delete_field(token: str, app_token: str, table_id: str, field_id: str) -> dict:
    client = get_client()
    request = (
        DeleteAppTableFieldRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .field_id(field_id)
        .build()
    )
    response = client.bitable.v1.app_table_field.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"删除字段失败: code={response.code}, msg={response.msg}")
    return {"field_id": field_id, "deleted": True}


def list_views(token: str, app_token: str, table_id: str, *, page_size: int = 50) -> list[dict]:
    client = get_client()
    all_items: list[dict] = []
    page_token: str | None = None
    while True:
        builder = (
            ListAppTableViewRequest.builder()
            .app_token(app_token)
            .table_id(table_id)
            .page_size(page_size)
        )
        if page_token:
            builder = builder.page_token(page_token)
        response = client.bitable.v1.app_table_view.list(builder.build(), make_option(token))
        if not response.success():
            if response.code == 99991679:
                raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
            raise RuntimeError(f"获取视图列表失败: code={response.code}, msg={response.msg}")
        for v in response.data.items or []:
            all_items.append({
                "view_id": getattr(v, "view_id", ""),
                "view_name": getattr(v, "view_name", ""),
                "view_type": getattr(v, "view_type", ""),
            })
        if not response.data.has_more:
            break
        page_token = response.data.page_token
    return all_items


def create_view(
    token: str,
    app_token: str,
    table_id: str,
    view_name: str,
    *,
    view_type: str = "grid",
) -> dict:
    client = get_client()
    request = (
        CreateAppTableViewRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .request_body(ReqView.builder().view_name(view_name).view_type(view_type).build())
        .build()
    )
    response = client.bitable.v1.app_table_view.create(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"创建视图失败: code={response.code}, msg={response.msg}")
    v = response.data.view
    return {
        "view_id": getattr(v, "view_id", ""),
        "view_name": getattr(v, "view_name", ""),
        "view_type": getattr(v, "view_type", ""),
    }


def delete_view(token: str, app_token: str, table_id: str, view_id: str) -> dict:
    client = get_client()
    request = (
        DeleteAppTableViewRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .view_id(view_id)
        .build()
    )
    response = client.bitable.v1.app_table_view.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"删除视图失败: code={response.code}, msg={response.msg}")
    return {"view_id": view_id, "deleted": True}


def delete_table(token: str, app_token: str, table_id: str) -> dict:
    client = get_client()
    request = (
        DeleteAppTableRequest.builder()
        .app_token(app_token)
        .table_id(table_id)
        .build()
    )
    response = client.bitable.v1.app_table.delete(request, make_option(token))
    if not response.success():
        if response.code == 99991679:
            raise InsufficientScopeError(MODULE_SCOPES.get("bitable", []), module="bitable")
        raise RuntimeError(f"删除数据表失败: code={response.code}, msg={response.msg}")
    return {"table_id": table_id, "deleted": True}
