package com.ssyf.test.helper;

import cn.hutool.json.JSONUtil;
import com.ssyf.common.core.util.MapUtil;
import com.ssyf.common.security.constant.SecurityConstant;
import com.ssyf.common.security.domain.ClientInfo;

import com.ssyf.common.web.apicode.ApiCodeUser;
import com.ssyf.common.web.result.Result;
import com.ssyf.common.web.result.ResultHelper;
import com.ssyf.test.bo.MockUser;
import lombok.Builder;
import org.junit.jupiter.api.Assertions;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MockMvc;

import java.util.concurrent.Callable;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * 模拟帮助类
 *
 * @author huwei
 * @date 2024/2/26 14:40
 */
@Builder
public class MockHelper {

    /**
     * 线程共享变量
     */
    private static ThreadLocal<MockUser> local = new InheritableThreadLocal<>();

    private MockMvc mockMvc;
    /**
     * 证书类型
     */
    private String certificateType;
    /**
     * 证书事件
     */
    private String certificateEvent;
    /**
     * 证书内容
     */
    private String certificateContent;
    /**
     * 登录回调
     */
    private Callable<String> loginCall;
    /**
     * 注册回调
     */
    private Callable registerCall;
    /**
     * 注册标识
     */
    @Builder.Default
    private boolean register = true;
    /**
     * 模拟
     */
    private static final String DEFAULT_MOCK_ACCOUNT_PASSWORD = "mockAccountForTest";

    /**
     * 获取模拟用户
     * @return
     */
    public static MockUser getMockUser(){
        return local.get();
    }

    /**
     * 清理模拟用户
     */
    public static void remove(){
        local.remove();
    }

    /**
     * 获取模拟用户token
     * @return
     */
    public static String getMockUserToken(){
        return local.get() != null ? local.get().getToken() : null;
    }

    /**
     * 获取模拟用户信息
     * @return
     */
    public static ClientInfo getMockUserClientInfo(){
        return local.get() != null ? local.get().parseAndGet() : null;
    }

    /**
     * 获取模拟用户id
     * @return
     */
    public static String getMockUserId(){
        return local.get() != null ? local.get().parseAndGet().getId().toString() : null;
    }

    /**
     * 模拟游客用户
     *
     * @param request
     * @return
     */
    public MockHttpServletRequest mockVisitor(MockHttpServletRequest request) {
        Assertions.assertNotNull(mockMvc);
        String token = getVisitorTokenAndAssertNotNull();
        request.addHeader("Authorization", token);
        return request;
    }

    /**
     * 模拟游客和证书信息
     *
     * @param request
     * @return
     */
    public MockHttpServletRequest mockVisitorAndCertificate(MockHttpServletRequest request) {
        Assertions.assertNotNull(mockMvc);
        String token = getVisitorTokenAndAssertNotNull();
        request.addHeader("Authorization", token);
        String certificateString = TestHelper.mockCertificate(certificateType, certificateEvent, certificateContent, token);
        request.addHeader(SecurityConstant.CERTIFICATE_NAME, certificateString);
        return request;
    }

    /**
     * 模拟用户；
     * 注意：：模拟默认用户时，会模拟注册用户然后在进行登录，若多次调用该方法可能会造成用户已注册提示；可在后续调用中把register设置为false即可
     * @param request
     * @return
     */
    public MockHttpServletRequest mockUser(MockHttpServletRequest request) {
        Assertions.assertNotNull(mockMvc);
        String token = getUserTokenAndAssertNotNull();
        request.addHeader("Authorization", token);
        return request;
    }

    /**
     * 模拟用户和证书信息；
     * 注意：：模拟默认用户时，会模拟注册用户然后在进行登录，若多次调用该方法可能会造成用户已注册提示；可在后续调用中把register设置为false即可
     * @param request
     * @return
     */
    public MockHttpServletRequest mockUserAndCertificate(MockHttpServletRequest request) {
        Assertions.assertNotNull(mockMvc);
        String token = getUserTokenAndAssertNotNull();
        request.addHeader("Authorization", token);
        String certificateString = TestHelper.mockCertificate(certificateType, certificateEvent, certificateContent, token);
        request.addHeader(SecurityConstant.CERTIFICATE_NAME, certificateString);
        return request;
    }

    /**
     * 模拟默认用户注册
     */
    private void mockRegisterDefaultUser() {
        String content = JSONUtil.toJsonStr(MapUtil.builder().put("username", DEFAULT_MOCK_ACCOUNT_PASSWORD).put("password", DEFAULT_MOCK_ACCOUNT_PASSWORD).build());
        String responseString = null;
        try {
            responseString = mockMvc.perform(
                    post("/share/user/register/username")
                            .content(content)
                            .contentType(MediaType.APPLICATION_JSON)
                            .with(MockHelper.builder().mockMvc(mockMvc).certificateType("captcha").build()::mockVisitorAndCertificate)
                            .accept(MediaType.APPLICATION_JSON)
            )
//                    .andDo(MockMvcResultHandlers.print())
                    .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
        } catch (Exception e) {
        }
        Result result = TestHelper.toResult(responseString);
        Assertions.assertTrue(ResultHelper.isSuccess(result) || result.getCode().equals(ApiCodeUser.PARAMETER_ERROR.getCode()));
    }

    /**
     * 模拟默认用户登录；
     *
     * @return
     */
    private String mockLoginDefaultUser() {
        String content = JSONUtil.toJsonStr(MapUtil.builder().put("username", DEFAULT_MOCK_ACCOUNT_PASSWORD).put("password", DEFAULT_MOCK_ACCOUNT_PASSWORD).build());
        String responseString = null;
        try {
            responseString = mockMvc.perform(
                    post("/share/user/login/username")
                            .content(content)
                            .contentType(MediaType.APPLICATION_JSON)
                            .with(MockHelper.builder().mockMvc(mockMvc).certificateType("captcha").build()::mockVisitorAndCertificate)
                            .accept(MediaType.APPLICATION_JSON)
            )
//                    .andDo(MockMvcResultHandlers.print())
                    .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
        } catch (Exception e) {
        }
        Result result = JSONUtil.toBean(responseString, Result.class);
        TestHelper.assertResultSuccess(result);
        return result.getData().toString();
    }

    /**
     * 获取用户token并断言不为空
     * @return
     */
    private String getUserTokenAndAssertNotNull() {
        String token = null;
        //模拟登录
        try {
            //模拟注册用户
            if (this.register && this.registerCall != null) {
                this.registerCall.call();
            } else if (this.register && this.loginCall == null) {
                this.mockRegisterDefaultUser();
            }
            token = this.loginCall != null ? this.loginCall.call() : this.mockLoginDefaultUser();
        } catch (Exception e) {
        }
        Assertions.assertNotNull(token);
        //缓存token
        local.set(new MockUser().setToken(token));
        return token;
    }

    /**
     * 获取游客token并断言不为空
     * @return
     */
    private String getVisitorTokenAndAssertNotNull() {
        String token = null;
        try {
            token = TestHelper.getVisitorToken(mockMvc);
        } catch (Exception e) {
        }
        Assertions.assertNotNull(token);
        return token;
    }


}
