# -*- coding: utf-8 -*-
"""Func LDAP 认证及属性加工入口；core 脚本由同一份 Studio 执行器生成。"""
import copy
import json
import time

import __core as core


def _settings():
    # Func 环境变量与 Studio 配置各自部署；不接收请求传入的 secret 或目录配置。
    secret = DFF.ENV("LDAP_CLIENT_DEMO_SECRET", "")
    value = DFF.ENV("LDAP_CLIENT_DEMO_CONFIG", {})
    if isinstance(value, str):
        value = json.loads(value)
    if not isinstance(value, dict):
        raise core.LDAPAuthFailure("config")
    config = copy.deepcopy(value)
    config["enable"] = True
    config["authMode"] = "builtin"
    config.setdefault("mapping", {"exterId": "uid", "username": "uid", "email": "mail"})
    return secret, config


def _remaining(expires):
    def remaining():
        value = expires - time.monotonic()
        if value <= 0:
            raise core.LDAPAuthFailure("auth_timeout")
        return value
    return remaining


def _run(body, transform_only=False):
    try:
        secret, config = _settings()
        payload = core.decode_body(body, secret)
        if transform_only:
            if set(payload) != {"username", "userinfo"} or not isinstance(payload["username"], str):
                raise core.LDAPAuthFailure("protocol_invalid")
            original = core.validate_userinfo(payload["userinfo"], config["mapping"])
            # 客户属性加工写在此处；仅返回顶层补丁，缺字段仍由 Studio 保留。
            patch = {}
            core.merge_transform(original, patch, config["mapping"])
            return {"status": "success", "userinfo": patch}
        if set(payload) != {"username", "password"}:
            raise core.LDAPAuthFailure("protocol_invalid")
        if not config.get("server", {}).get("host") and not config.get("server", {}).get("hosts"):
            raise core.LDAPAuthFailure("config")
        expires = time.monotonic() + core.auth_timeout(config)
        userinfo, _ = core.authenticate(config, payload["username"], payload["password"], _remaining(expires))
        return {"status": "success", "userinfo": userinfo}
    except core.LDAPAuthFailure as error:
        return {"status": "denied" if error.kind in ("denied", "missing") else "error"}
    except Exception:
        # 不输出原始异常、请求体、userinfo 或配置；Func 外层监控也须禁采原始参数。
        return {"status": "error"}


# Studio 直连入口不强制绑定 Func API 认证；访问范围由部署侧限制在受控内网。
@DFF.API("LDAP 标准认证", require_api_auth=False, fixed_func_api_async=False, timeout=20, cache_result=0)
def authenticate(body=""):
    return _run(body)


@DFF.API("LDAP 属性加工", require_api_auth=False, fixed_func_api_async=False, timeout=20, cache_result=0)
def transform(body=""):
    return _run(body, transform_only=True)


def self_test():
    """仅用合成字符串验证协议，不连接 LDAP、不返回或打印凭证。"""
    payload = {"username": "synthetic-user", "password": "synthetic-password"}
    for secret in ("", "synthetic-protocol-secret"):
        body = core.encode_body(payload, secret)
        assert isinstance(body, str)
        assert core.decode_body(body, secret) == payload
    return {"status": "success", "checks": ["plain_body_string", "encrypted_body_string"]}
