package com.aip.gateway.api.service.impl;

import com.aip.gateway.api.config.ApplicationConfig;
import com.aip.gateway.api.dao.mapper.BaseDbMapper;
import com.aip.gateway.api.dto.LinkedApiKeyDto;
import com.aip.gateway.api.dto.LinkedDecryptKeyDto;
import com.aip.gateway.api.dto.LinkedServerDto;
import com.aip.gateway.api.dto.LinkedSystemDto;
import com.aip.gateway.api.entity.*;
import com.aip.gateway.api.model.app.AipSettings;
import com.aip.gateway.api.model.response.GeneralResponse;
import com.aip.gateway.api.repository.AppRepository;
import com.aip.gateway.api.service.IApiDbService;
import com.aip.gateway.api.utils.StringUtils;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@RequiredArgsConstructor
@Service
public class ApiDbService implements IApiDbService {

    private final ApplicationConfig config;
    private final AppRepository appRepo;
    private final BaseDbMapper mapper;

    @PostConstruct
    private void init() {

        initAipConfig();
        initLinkedApiKeys();
        initLinkedDecryptKeys();
    }

    private void initAipConfig() {
        AipSettings settings = new AipSettings();
        ConcurrentHashMap<String, String> supportedFileExtMap = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, String> protectedFileExtMap = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, String> supportedDelFileExtMap = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, String> protectedDelFileExtMap = new ConcurrentHashMap<>();

        List<TbAipConfig> lists = loadAipConfig();
        lists.forEach(obj -> {
            settings.setValue(obj.getConfigKey(), obj.getConfigValue());
        });
        this.appRepo.setSettings(settings);

        List<String> supportedLists = StringUtils.split(settings.getSupportedFileExt().trim(), ";");
        supportedLists.forEach(ext -> {
            if (ext.trim().isEmpty()) return;
            supportedFileExtMap.put(ext.trim(), ext.trim());
            protectedFileExtMap.put(ext.trim(), ext.trim());
            supportedDelFileExtMap.put(ext.trim(), ext.trim());
            protectedDelFileExtMap.put(ext.trim(), ext.trim());
        });

        List<String> protectedLists = StringUtils.split(settings.getProtectedFileExt().trim(), ";");
        protectedLists.forEach(protectedExt -> {
            if (protectedExt.trim().isEmpty()) return;
            List<String> extLists = StringUtils.split(protectedExt.trim(), "-");
            if (extLists.size() != 2) return;
            protectedFileExtMap.put(extLists.get(0), extLists.get(1));
            protectedDelFileExtMap.remove(extLists.get(0));
            protectedDelFileExtMap.put(extLists.get(1), extLists.get(0));
        });
        this.appRepo.setSupportedFileExtMap(supportedFileExtMap);
        this.appRepo.setProtectedFileExtMap(protectedFileExtMap);
        this.appRepo.setSupportedDelFileExtMap(supportedDelFileExtMap);
        this.appRepo.setProtectedDelFileExtMap(protectedDelFileExtMap);

        log.info("settings: {}", settings);
        log.info("supportedFileExtMap: {}", this.appRepo.getSupportedDelFileExtMap());
        log.info("protectedFileExtMap: {}", this.appRepo.getProtectedFileExtMap());
        log.info("supportedDelFileExtMap: {}", this.appRepo.getSupportedDelFileExtMap());
        log.info("protectedDelFileExtMap: {}", this.appRepo.getProtectedDelFileExtMap());
    }

    private void initLinkedApiKeys() {
        ConcurrentHashMap<String, LinkedApiKeyDto> keyMap = new ConcurrentHashMap<>();
        List<TbLinkedApiKey> lists = loadLinkedApiKeys();
        log.info("ApiKeyLists: {}", lists);
        lists.forEach(obj -> {
            LinkedApiKeyDto key = keyMap.get(obj.getApiKey());
            log.info("ApiKey: {}, {}", obj.getApiKey(), obj.getServerIpAddr());
            if (key == null) {
                LinkedApiKeyDto newKey = obj.toDto();
                keyMap.put(obj.getApiKey(), newKey);
                newKey.getServerMap().put(newKey.getServerIpAddr(), newKey);
                log.info("New ApiKey: {}, {}", newKey.getApiKey(), newKey.getServerIpAddr());
            }
            else {
                LinkedApiKeyDto newKey = obj.toDto();
                log.info("INN ApiKey: {}, {}", newKey.getApiKey(), newKey.getServerIpAddr());
                key.getServerMap().put(newKey.getServerIpAddr(), newKey);
            }
        });
        this.appRepo.setApiKeyMap(keyMap);
        this.appRepo.getApiKeyMap().forEach((key, obj) -> {
            log.info("ApiKeyMap: {}, {}.", key, obj);
            obj.getServerMap().forEach((ip, server) -> {
                log.info("         : {}, {}, {}, {}.", key, ip, server, server.getExpiredAt());
            });
        });
    }

    private void initLinkedDecryptKeys() {
        ConcurrentHashMap<String, LinkedDecryptKeyDto> keyMap = new ConcurrentHashMap<>();
        List<TbLinkedDecryptKey> lists = loadLinkedDecryptKeys();
        lists.forEach(obj -> {
            LinkedDecryptKeyDto key = keyMap.get(obj.getDecryptKey());
            if (key == null) {
                LinkedDecryptKeyDto newKey = obj.toDto();
                keyMap.put(obj.getDecryptKey(), newKey);
                newKey.getServerMap().put(newKey.getServerIpAddr(), newKey);
            }
            else {
                LinkedDecryptKeyDto newKey = obj.toDto();
                key.getServerMap().put(newKey.getServerIpAddr(), newKey);
            }
        });
        this.appRepo.setDecryptKeyMap(keyMap);
        this.appRepo.getDecryptKeyMap().forEach((key, obj) -> {
            log.info("DecryptKeyMap: {}, {}.", key, obj);
            obj.getServerMap().forEach((ip, server) -> {
                log.info("             : {}, {}, {}, {}.", key, ip, server, server.getExpiredAt().toString());
            });
        });
    }

    @Override
    public GeneralResponse reloadDatabase() {
        initLinkedApiKeys();
        initLinkedDecryptKeys();
        return GeneralResponse.builder()
                .errorCode(0)
                .errorMessage("success")
                .effectCount(this.appRepo.getApiKeyMap().size() + this.appRepo.getDecryptKeyMap().size())
                .build();
    }

    @Override
    public List<LinkedSystemDto> getLinkedSystems() {
        List<LinkedSystemDto> result = new ArrayList<>();
        List<TbLinkedSystem> lists = this.mapper.selectLinkedSystems();
        lists.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    @Override
    public List<LinkedServerDto> getLinkedServers() {
        List<LinkedServerDto> result = new ArrayList<>();
        List<TbLinkedServer> lists = this.mapper.selectLinkedServers();
        lists.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    @Override
    public List<LinkedApiKeyDto> getLinkedApiKeys() {
        List<LinkedApiKeyDto> result = new ArrayList<>();
        List<TbLinkedApiKey> lists = this.mapper.selectLinkedApiKeys();
        lists.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    @Override
    public List<LinkedDecryptKeyDto> getLinkedDecryptKeys() {
        List<LinkedDecryptKeyDto> result = new ArrayList<>();
        List<TbLinkedDecryptKey> lists = this.mapper.selectLinkedDecryptKeys();
        lists.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    @Override
    public List<TbAipConfig>  loadAipConfig() {
        Map<String, Integer> param = new HashMap<>();
        param.put("AIP_SERVER_ID", this.config.getAipServerId());
        return this.mapper.selectAipConfig(param);
    }
    @Override
    public List<TbLinkedApiKey>  loadLinkedApiKeys() {
        return this.mapper.selectLinkedApiKeys();
    }
    @Override
    public List<TbLinkedDecryptKey>  loadLinkedDecryptKeys() {
        return this.mapper.selectLinkedDecryptKeys();
    }

}
