package com.yuchen.saas.park.service.service.door.communication.impl;

import com.alibaba.fastjson.JSON;
import com.thinkunion.sdk.iot.IoTClient;
import com.thinkunion.sdk.iot.bean.api.CallingDeviceServiceRequest;
import com.thinkunion.sdk.iot.bean.api.CallingDeviceServiceResponse;
import com.yuchen.saas.park.api.util.JacksonUtils;
import com.yuchen.saas.park.service.iot.IoTRemoteService;
import com.yuchen.saas.park.service.service.door.communication.DoorAccessControlCommunicationComponent;
import com.yuchen.saas.park.service.service.door.communication.domain.*;
import com.yuchen.saas.park.service.service.door.communication.domain.ResponseHolder.ResponseDetails;
import com.yuchen.saas.park.service.service.door.communication.domain.card.AddCardParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.card.DeleteCardParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.card.QueryCardParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.card.UpdateCardParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.user.AddUserFacialParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.user.AddUserParameter;
import com.yuchen.saas.park.service.service.door.communication.domain.user.DeleteUserParameter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.util.Optional;

import static com.yuchen.saas.park.service.service.door.communication.domain.CommandAction.*;

@Component
@Slf4j
@RequiredArgsConstructor
public class DoorAccessControlCommunicationComponentImpl implements DoorAccessControlCommunicationComponent {
    private final IoTClient client;

    @Override
    public CallResult call(CommonsParameter commonsParameter, CommandAction action, CommandParameter commandParameter) {
        log.info("======门禁定时调用iot原始传参 {}:\n{}\n{}", action, commonsParameter, commandParameter);

        try {
            val request = this.prepareRequest(commonsParameter, action, commandParameter);

            log.info("====== 定时调用iot传参：{}", JSON.toJSONString(request));

            val response = client.send(request);

            log.info("====== 定时调用iot返回结果：{}",JSON.toJSONString(response));

            return this.handleResponse(response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);

            return CallResult.error(e.getMessage());
        }
    }

    @Override
    public CallResult call(CommonsParameter commonsParameter, CommandParameter commandParameter) {
        return this.call(commonsParameter, parse(commandParameter.getClass()), commandParameter);
    }

    @Override
    public CallResult addCard(CommonsParameter commonsParameter, AddCardParameter o) {
        return this.call(commonsParameter, ADD_CARD, o);
    }

    @Override
    public CallResult updateCard(CommonsParameter commonsParameter, UpdateCardParameter o) {
        return this.call(commonsParameter, UPDATE_CARD, o);
    }

    @Override
    public CallResult deleteCard(CommonsParameter commonsParameter, DeleteCardParameter o) {
        return this.call(commonsParameter, DELETE_CARD, o);
    }

    @Override
    public CallResult queryCard(CommonsParameter commonsParameter, QueryCardParameter o) {
        return this.call(commonsParameter, QUERY_CARD, o);
    }

    @Override
    public CallResult addUser(CommonsParameter commonsParameter, AddUserParameter o) {
        return this.call(commonsParameter, ADD_USER, o);
    }

    @Override
    public CallResult deleteUser(CommonsParameter commonsParameter, DeleteUserParameter o) {
        return this.call(commonsParameter, DELETE_USER, o);
    }

    @Override
    public CallResult addUserFacial(CommonsParameter commonsParameter, AddUserFacialParameter o) {
        return this.call(commonsParameter, ADD_USER_FACIAL, o);
    }

    private CallResult handleResponse(CallingDeviceServiceResponse response) {
        log.debug("=== 原始响应: {} ===", response);

        if (!(String.valueOf(HttpStatus.SC_OK)).equals(response.getCode())) {
            return CallResult.error(response.getMessage());
        }

        val holder = JacksonUtils.convert(response.getData(), ResponseHolder.class);

        log.debug("=== 响应: {} ===", holder);

        val details = holder.getDetails();
        if (details == null || details.getCode() != 0) {
            log.warn("=== 调用失败: {} ===", details);
            return CallResult.error(Optional.ofNullable(details).map(ResponseDetails::getMessage).orElse(holder.getMessage()));
        }

        log.info("=== 调用成功 ===");

        return CallResult.successful();
    }

    /**
     * @see IoTRemoteService
     */
    private CallingDeviceServiceRequest prepareRequest(CommonsParameter commonsParameter, CommandAction action, CommandParameter commandParameter) {
        val request = new CallingDeviceServiceRequest();
        request.setProductKey(commonsParameter.getProductKey());
        request.setDeviceName(commonsParameter.getDeviceName());
        request.setIdentifier(action.getAction());

        val map = commandParameter.encoding();

        log.debug("====== 参数:\n{}", JacksonUtils.objectToString(map));

        map.forEach((k, v) -> request.addParam(k, isJavaLang(v) ? v.toString() : JacksonUtils.objectToString(v)));

        request.queryParam("Args", JacksonUtils.objectToString(map)); // another way for local

        return request;


        /*val request = new CallingDeviceServiceRequest();
        request.setProductKey(commonsParameter.getProductKey());
        request.setDeviceName(commonsParameter.getDeviceName());
        request.setIdentifier(action.getAction());

        val map = commandParameter.encoding();

        log.info("====== params: {}", JSON.toJSONString(map));

        //map.forEach((k, v) -> request.addParam(k, isJavaLang(v) ? v.toString() : JSON.toJSONString(v)));

        //JSONObject paramsObject = JSON.parseObject(JSON.toJSONString(params.get("params")));
        map.forEach((key, obj) -> {
            if (obj instanceof JSON) {
                JSON json = (JSON) obj;
                request.addParam(key, json.toJSONString());
            } else {
                request.addParam(key, obj.toString());
            }
        });

        // request.queryParam("Args", JacksonUtils.objectToString(map)); // another way for local

        return request;*/
    }

    private static boolean isJavaLang(Object o) {
        return o.getClass().getName().startsWith("java.lang");
    }
}
