package com.cool.store.service.xinfa;

import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.RedisConstant;
import com.cool.store.dto.huoma.*;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.SpecialTagEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.response.ResponseResult;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.constant.Constants;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author suzhuhong
 * @Date 2025/11/4 15:47
 * @Version 1.0
 */
@Service
@Slf4j
public class XinFaDeviceService {

    @Value("${huoMa.direct.stores.account}")
    private String huoMaDirectStoresAccount;
    @Value("${huoMa.direct.stores.password}")
    private String huoMaDirectStoresPassword;
    @Value("${huoMa.franchise.stores.account}")
    private String huoMaFranchiseStoresAccount;
    @Value("${huoMa.franchise.stores.password}")
    private String huoMaFranchiseStoresPassword;
    @Value("${huoMa.restaurant.stores.account}")
    private String huoMaRestaurantStoresAccount;
    @Value("${huoMa.restaurant.stores.password}")
    private String huoMaRestaurantStoresPassword;
    @Resource
    RedisUtilPool redisUtilPool;
    @Value("${huoMa.token.url}")
    private String huoMaTokenUrl;
    @Value("${huoMa.get.point.terminal.url}")
    private String huoMaGetPointTerminalUrl;
    @Value("${huoMa.id.url}")
    private String huoMaGetStoreIdUrl;
    @Value("${huoMa.store.device.detail.url}")
    private String huoMaGetStoreXinFaDeviceDetailUrl ;
    @Value("${huoMa.get.tag.url}")
    private String huoMaGetTagUrl;
    @Value("${huoMa.get.program.url}")
    private String huoMaGetProgramUrl;
    @Value("${huoMa.get.publish.url}")
    private String huoMaGetPublishUrl;


    private final Map<String, HuoMaAccountDTO> accountMap = new HashMap<>();

    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    @PostConstruct
    public void initAccountMap() {
        accountMap.put("restaurant", new HuoMaAccountDTO(huoMaRestaurantStoresAccount, huoMaRestaurantStoresPassword));
        accountMap.put("direct", new HuoMaAccountDTO(huoMaDirectStoresAccount, huoMaDirectStoresPassword));
        accountMap.put("franchise", new HuoMaAccountDTO(huoMaFranchiseStoresAccount, huoMaFranchiseStoresPassword));
    }
    
    public String getStoreToken(String account, String password) {
        String key = MessageFormat.format(RedisConstant.HUO_MA_TOKEN, account);
        String accessToken = redisUtilPool.getString(key);
        if (accessToken != null) {
            return accessToken;
        }
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("account", account);
        requestBody.put("password", password);
        String responseBody = sendPostRequest(JSONObject.toJSONString(requestBody), huoMaTokenUrl);
        try{
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(responseBody);
            String token = rootNode.path("data").path("token").asText();
            //缓存60秒
            redisUtilPool.setString(key, token,60*60);
            return  token;
        }catch (Exception e){
            log.error("解析获取token失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
        }
        return null;
    }

    
    public List<StoreEquipmentDTO> getStoreEquipmentDataByStoreNumList(List<String> storeNumList, String  token) {
        Map<String,List<String>> requestBody = new HashMap<>();
        requestBody.put("codeList", storeNumList);
        String responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(requestBody), huoMaGetPointTerminalUrl,token);
        try{
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(responseBody);
            return mapper.convertValue(rootNode.get("data"),
                    mapper.getTypeFactory().constructCollectionType(List.class, StoreEquipmentDTO.class));
        }catch (Exception e){
            log.error("解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
        }
        return Collections.emptyList();
    }


    
    public Integer getStoreIdByStoreNum(String storeNum, String token) {
        String houMaStoreId = redisUtilPool.hashGet(RedisConstant.HUO_MA_STORE_ID, storeNum);
        if (houMaStoreId != null) {
            try {
                return Integer.valueOf(houMaStoreId);
            } catch (NumberFormatException e) {
                // 如果缓存中的数据格式不正确，继续执行正常逻辑
                log.warn("Redis缓存中的门店ID格式不正确，storeNum: {}", storeNum);
            }
        }
        StoreRequestDTO requestBody = new StoreRequestDTO("point_report", 0, 10, storeNum);
        String responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(requestBody), huoMaGetStoreIdUrl,token);
        try{
            Integer storeId = extractIdsFromResponse(responseBody);
            redisUtilPool.hashSet(RedisConstant.HUO_MA_STORE_ID, storeNum, storeId.toString());
            return storeId;
        }catch (Exception e){
            log.error("解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
        }
        return null;
    }


    public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetail(String storeNum) {
        String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum);
        if (StringUtils.isNotBlank(source)) {
            HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
            if (Objects.nonNull(huoMaAccountDTO)) {
                String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
                List<StoreXinFaDeviceDetail> deviceDetailDetail = getStoreXinFaDeviceDetailDetail(storeNum, token);
                if (CollectionUtils.isNotEmpty(deviceDetailDetail)) {
                    return deviceDetailDetail;
                }
            }
        }
        for (Map.Entry<String, HuoMaAccountDTO> entry : accountMap.entrySet()) {
            HuoMaAccountDTO huoMaAccountDTO = entry.getValue();
            if (!huoMaAccountDTO.getIsQuery()) {
                String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
                List<StoreXinFaDeviceDetail> deviceDetailDetail = getStoreXinFaDeviceDetailDetail(storeNum, token);
                if (CollectionUtils.isNotEmpty(deviceDetailDetail)) {
                    redisUtilPool.hashSet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum, entry.getKey(), Constants.REFRESH_TOKEN_EXPIRE);
                    return deviceDetailDetail;
                }
            }
        }
        return Collections.emptyList();
    }


    public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetailDetail(String storeNum, String token) {
        Integer storeIdByStoreNum = getStoreIdByStoreNum(storeNum, token);
        return getStoreXinFaDeviceDetailByPointId(storeIdByStoreNum, token);
    }


    public List<TagDetailDTO> getAccountAllTags(String storeNum){
        String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum);
        //获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
        if (StringUtils.isEmpty(source)){
            log.info("门店没有找到对应的账号,storeNum: {}", storeNum);
            return new ArrayList<>();
        }
        HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
        if (Objects.nonNull(huoMaAccountDTO)){
            huoMaAccountDTO.setIsQuery(true);
            String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
            TagDTO tagDTO = new TagDTO("DEFAULT", 0, 30,"program");
            String responseBody = null;
            try{
                responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(tagDTO), huoMaGetTagUrl,token);
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(responseBody);

                // 直接转换整个数组
                return mapper.convertValue(
                        rootNode.path("data").path("content"),
                        mapper.getTypeFactory().constructCollectionType(List.class, TagDetailDTO.class)
                );
            }catch (Exception e){
                log.error("getAccountAllTags解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
            }
        }
        return new ArrayList<>();
    }


    /**
     * 获取账号下指定标签的id 只需要一下四个
     * @param storeNum
     * @return
     */
    public List<Integer> getAccountSpecialTagIds( String storeNum,List<String> tagList) {
        List<TagDetailDTO> accountAllTags = getAccountAllTags(storeNum);
        if (accountAllTags != null) {
            return accountAllTags.stream()
                    .filter(tag -> tag.getName() != null &&
                            tagList.contains(tag.getName()))
                    .map(TagDetailDTO::getId)
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }


    public List<ProgramResponseDTO> getProgramList(ProgramReqDTO programReqDTO) {
        if (StringUtils.isEmpty(programReqDTO.getStoreCode())){
            log.info("门店没有找到对应的账号,storeNum: {}", programReqDTO.getStoreCode());
            return new ArrayList<>();
        }
        //先查询门店是属于哪个账号下
        String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, programReqDTO.getStoreCode());
        //获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
        if (StringUtils.isEmpty(source)){
            log.info("门店没有找到对应的账号,storeNum: {}", programReqDTO.getStoreCode());
            return new ArrayList<>();
        }
        HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
        if (huoMaAccountDTO!=null){
            String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
            String responseBody = null;
            try{
                responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(programReqDTO), huoMaGetProgramUrl,token);
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(responseBody);

                // 直接转换整个数组
                return mapper.convertValue(
                        rootNode.path("data").path("content"),
                        mapper.getTypeFactory().constructCollectionType(List.class, ProgramResponseDTO.class)
                );
            }catch (Exception e){
                log.error("getProgramList 解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
            }
        }
        return new ArrayList<>();
    }

    public Boolean publish(PublishDTO publishDTO){
        if (StringUtils.isEmpty(publishDTO.getStoreCode())){
            log.info("门店没有找到对应的账号，发布失败,storeNum: {}", publishDTO.getStoreCode());
            return Boolean.FALSE;
        }
        String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, publishDTO.getStoreCode());
        //获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
        if (StringUtils.isEmpty(source)){
            log.info("门店没有找到对应的账号,storeNum: {}", publishDTO.getStoreCode());
            return Boolean.FALSE;
        }
        HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
        if (huoMaAccountDTO!=null){
            String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
            String responseBody = null;
            try{
                responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(publishDTO), huoMaGetPublishUrl,token);
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(responseBody);
                int code = rootNode.get("code").asInt();
                if (code != 0) {
                    throw new RuntimeException("发布失败");
                }
                // 直接转换整个数组
                log.info("发布成功 deviceId:{},storeCode:{}",JSONObject.toJSONString(publishDTO.getDeviceIdList()), publishDTO.getStoreCode() );
                return Boolean.TRUE;
            }catch (Exception e){
                log.error("发布失败, url:{}, responseBody:{}", huoMaTokenUrl, responseBody);
            }
        }
        return Boolean.FALSE;
    }


    
    public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetailByPointId(Integer pointId, String token) {
        if (pointId != null){
            StoreXinFaDetailRequestDTO storeXinFaDetailRequestDTO = new StoreXinFaDetailRequestDTO(0, 10, pointId);
            String responseBody = null;
            try{
                responseBody = sendPostRequestNoRetryByToken(JSONObject.toJSONString(storeXinFaDetailRequestDTO), huoMaGetStoreXinFaDeviceDetailUrl,token);
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(responseBody);

                // 直接转换整个数组
                return mapper.convertValue(
                        rootNode.path("data").path("content"),
                        mapper.getTypeFactory().constructCollectionType(List.class, StoreXinFaDeviceDetail.class)
                );
            }catch (Exception e){
                log.error("getStoreXinFaDeviceDetailByPointId解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
            }
            return null;
        }
        return null;
    }

    private Integer extractIdsFromResponse(String jsonResponse) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(jsonResponse);
        // 遍历dataList数组提取ID
        JsonNode dataList = rootNode.path("data").path("dataList");
        for (JsonNode item : dataList) {
            if (item.has("ID")) {
                //取出第一个id
                return  item.get("ID").asInt();
            }
        }
        return null;
    }

    private String sendPostRequest(String requestBody, String requestUrl) {
        log.info("开始发送请求,url:{},requestBody:{}", requestUrl, requestBody);
        Request request = new Request.Builder()
                .url(requestUrl)
                .post(RequestBody.create(MediaType.parse("application/json"), requestBody))
                .build();
        return sendPost(requestUrl, request);
    }


    /**
     * 不重试 请求接口
     * @param requestBody
     * @param requestUrl
     * @param token
     * @return
     */
    private String sendPostRequestNoRetryByToken(String requestBody, String requestUrl, String token) {
        log.info("开始发送请求,url:{},requestBody:{}", requestUrl, requestBody);

        try {
            Request request = new Request.Builder()
                    .url(requestUrl)
                    .post(RequestBody.create(MediaType.parse("application/json"), requestBody))
                    .addHeader("token", token)
                    .build();

            String result = sendPost(requestUrl, request);

            // 检查是否token失效
            if (isTokenExpired(result)) {
                log.warn("Token已失效，清除缓存并重新获取token");
                // 清除对应账号的token缓存并获取新token
                String newToken = clearAccountTokenCacheForToken(token);

                if (newToken != null) {
                    // 使用新token重新发起一次请求
                    Request newRequest = new Request.Builder()
                            .url(requestUrl)
                            .post(RequestBody.create(MediaType.parse("application/json"), requestBody))
                            .addHeader("token", newToken)
                            .build();

                    result = sendPost(requestUrl, newRequest);
                } else {
                    throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "Token失效且无法获取新token");
                }
            }
            return result;
        } catch (Exception e) {
            log.error("请求异常，错误: {}", e.getMessage());
            throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求失败: " + e.getMessage());
        }
    }

    /**
     * 包含重试 适用于批量拉取数据 ，正常页面接口无需重试
     * @param requestBody
     * @param requestUrl
     * @param token
     * @return
     */
    private String sendPostRequestByToken(String requestBody, String requestUrl, String token) {
        log.info("开始发送请求,url:{},requestBody:{}", requestUrl, requestBody);

        int maxRetries = 3;
        for (int i = 0; i < maxRetries; i++) {
            try {
                Request request = new Request.Builder()
                        .url(requestUrl)
                        .post(RequestBody.create(MediaType.parse("application/json"), requestBody))
                        .addHeader("token", token)
                        .build();

                String result = sendPost(requestUrl, request);

                // 检查是否token失效
                if (isTokenExpired(result)) {
                    log.warn("Token已失效，正在清除缓存并重试，第{}次", i + 1);
                    // 直接清除对应账号的token缓存
                    token = clearAccountTokenCacheForToken(token);

                    // 抛出异常触发重试
                    throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "Token失效，需要重试");
                }

                return result;
            } catch (ServiceException e) {
                log.warn("请求失败，第{}次重试，错误: {}", i + 1, e.getMessage());
                if (i == maxRetries - 1) {
                    throw e;
                }
                try {
                    Thread.sleep(1000 * (i + 1));
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "重试被中断");
                }
            } catch (Exception e) {
                log.warn("请求异常，第{}次重试，错误: {}", i + 1, e.getMessage());
                if (i == maxRetries - 1) {
                    throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败: " + e.getMessage());
                }
                try {
                    Thread.sleep(1000 * (20*i + 1));
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "重试被中断");
                }
            }
        }
        throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败");
    }

    private boolean isTokenExpired(String responseBody) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(responseBody);
            int code = rootNode.path("code").asInt(-1);
            String msg = rootNode.path("msg").asText("");

            return code == 501;
        } catch (Exception e) {
            log.error("检查token失效状态时发生错误: {}", e.getMessage());
            return false;
        }
    }

    private String clearAccountTokenCacheForToken(String token) {
        try {
            // 遍历可能的账号，找到对应的缓存并删除
            String[] accounts = {
                    huoMaDirectStoresAccount,
                    huoMaFranchiseStoresAccount,
                    huoMaRestaurantStoresAccount
            };

            for (String account : accounts) {
                if (account != null) {
                    String key = MessageFormat.format(RedisConstant.HUO_MA_TOKEN, account);
                    String cachedToken = redisUtilPool.getString(key);
                    if (token.equals(cachedToken)) {
                        redisUtilPool.delKey(key);
                        log.info("已清除账号 {} 的token缓存", account);

                        // 根据账号类型获取对应的密码并重新获取token
                        String password = getPasswordForAccount(account);
                        if (password != null) {
                            String newToken = getStoreToken(account, password);
                            if (newToken != null) {
                                log.info("已为账号 {} 重新获取新的token", account);
                                return newToken;
                            }
                        }
                        break;
                    }
                }
            }
        } catch (Exception e) {
            log.error("清除token缓存或重新获取token失败: {}", e.getMessage());
        }
        return null;
    }


    private String getPasswordForAccount(String account) {
        if (account.equals(huoMaDirectStoresAccount)) {
            return huoMaDirectStoresPassword;
        } else if (account.equals(huoMaFranchiseStoresAccount)) {
            return huoMaFranchiseStoresPassword;
        } else if (account.equals(huoMaRestaurantStoresAccount)) {
            return huoMaRestaurantStoresPassword;
        }
        return null;
    }

    @NotNull
    private String sendPost(String requestUrl, Request request) {
        try (Response response = httpClient.newCall(request).execute()) {
            log.info("发起请求 time：{}", System.currentTimeMillis());
            if (!response.isSuccessful()) {
                log.info("HTTP请求失败，msg: " + response.message());
                throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR,
                        "HTTP请求失败，状态码: " + response.code());
            }
            String responseBody = response.body().string();
            log.info("请求成功responseBody:{}", JSONObject.toJSONString(responseBody));
            return responseBody;
        } catch (SocketTimeoutException e) {
            log.error("API调用超时 - URL: {}, 错误: {}", requestUrl, e.getMessage(), e);
            throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "接口调用超时: " + e.getMessage());
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("API调用异常 - URL: {}, 错误: {}", requestUrl, e.getMessage(), e);
            throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "接口调用异常: " + e.getMessage());
        }
    }

}
