package cn.ibizlab.core.data.service.impl;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

import cn.ibizlab.core.data.lite.LiteStorage;
import cn.ibizlab.core.data.model.DSLink;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.util.ObjectUtils;
import cn.ibizlab.util.errors.BadRequestAlertException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.context.annotation.Lazy;
import cn.ibizlab.core.data.domain.DSSetting;
import cn.ibizlab.core.data.filter.DSSettingSearchContext;
import cn.ibizlab.core.data.service.IDSSettingService;

import org.springframework.util.StringUtils;

import javax.sql.DataSource;


/**
 * 实体[数据源] 无存储服务对象接口实现
 */
@Slf4j
@Service
public class DSSettingServiceImpl implements IDSSettingService {


    @Autowired
    @Lazy
    private DSSettingServiceImpl proxy;

    public Map<String, DSLink> getAllConfigs()
    {
        Path path = Paths.get(LiteStorage.MODEL_PATH,"DATASOURCE.json");
        try {
            Map<String, DSLink> all=new LinkedHashMap<>();

            if(!Files.exists(path))
            {
                Files.write(path, JSON.toJSONBytes(new LinkedHashMap<String, DSLink>()), StandardOpenOption.CREATE);
            }
            else
            {
                LinkedHashMap<String, DSLink> map = JSON.parseObject(new String(Files.readAllBytes(path), StandardCharsets.UTF_8), new TypeReference<LinkedHashMap<String, DSLink>>(){});

                map.entrySet().forEach(item->{
                    String id=item.getKey();
                    DSLink config=item.getValue();
                    config.setName(id);
                    if(!all.containsKey(id))
                        all.put(id,config);
                    if(!all.containsKey(id.toLowerCase()))
                        all.put(id.toLowerCase(),config);
                    if(!ObjectUtils.isEmpty(config.getUsings()))
                    {
                        config.getUsings().forEach(using->{
                            if(!all.containsKey(using))
                                all.put(using,config);
                            if(!all.containsKey(using.toLowerCase()))
                                all.put(using.toLowerCase(),config);
                        });
                    }
                });
            }
            return all;
        } catch (Exception e) {
            throw new BadRequestAlertException("读写文件失败","DSLinkConfig",path.toString());
        }

    }

    @Override
    public boolean create(DSSetting et) {
        //代码实现

        return this.save(et);
    }

    public void createBatch(List<DSSetting> list){
        list.forEach(et->create(et));
    }

    @Override
    public boolean update(DSSetting et) {
        //代码实现


        this.save(et);

        synchronized (lock)
        {
            if (check.containsKey(et.getDsId()))
            {
                if(et.isDatabase()) {
                    DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
                    ds.removeDataSource(et.getDsId());
                }
                check.remove(et.getDsId());
            }
            if (!et.getDsId().equals(et.getDsId().toLowerCase()))
            {
                check.remove(et.getDsId().toLowerCase());
            }
            if(!ObjectUtils.isEmpty(et.getUsings()))
            {
                et.getUsings().forEach(using->{
                    check.remove(using);
                    if(!using.equals(using.toLowerCase()))
                        check.remove(using.toLowerCase());
                });
            }

        }
        buildDS(et);

        return true;
    }

    public void updateBatch(List<DSSetting> list){
        list.forEach(et->update(et));
    }

    @Override
    public boolean remove(String key) {

        DSSetting et = new DSSetting();
        synchronized (lock) {

            LinkedHashMap<String, DSLink> newConfigs = new LinkedHashMap<>();
            proxy.getAllConfigs().values().forEach(config -> {
                if (!newConfigs.containsKey(config.getName())) {
                    if (!config.getName().equals(key))
                        newConfigs.put(config.getName(), config);
                    else
                        et.setDSLinkConfig(config);
                }
            });
            Path path = Paths.get(LiteStorage.MODEL_PATH, "DATASOURCE.json");
            try {
                Files.write(path, JSON.toJSONBytes(newConfigs));
            } catch (Exception e) {
                throw new BadRequestAlertException("读写文件失败", "DSLinkConfig", path.toString());
            }

            if (!StringUtils.isEmpty(et.getDsId()))
            {
                if (check.containsKey(et.getDsId()))
                {
                    if(et.isDatabase()) {
                        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
                        ds.removeDataSource(et.getDsId());
                    }
                    check.remove(et.getDsId());
                }
                if (!et.getDsId().equals(et.getDsId().toLowerCase()))
                {
                    check.remove(et.getDsId().toLowerCase());
                }
                if(!ObjectUtils.isEmpty(et.getUsings()))
                {
                    et.getUsings().forEach(using->{
                        check.remove(using);
                        if(!using.equals(using.toLowerCase()))
                            check.remove(using.toLowerCase());
                    });
                }

            }
        }

        return true;
    }

    public void removeBatch(Collection<String> idList){
        idList.forEach(id->{
            remove(id);
        });
    }

    @Override
    public DSSetting get(String key) {
        DSSetting et = new DSSetting();

        Map<String, DSLink> allConfigs=proxy.getAllConfigs();
        if(allConfigs.containsKey(key))
            et.setDSLinkConfig(allConfigs.get(key));
        else
            return null;
        return et;
    }

    @Override
    public DSSetting getDraft(DSSetting et) {
        return et;
    }

    @Override
    public boolean checkKey(DSSetting et) {
        return false;
    }
    @Override
    @Transactional
    public boolean save(DSSetting et) {
        //代码实现
        synchronized (lock) {

            LinkedHashMap<String, DSLink> newConfigs = new LinkedHashMap<>();
            proxy.getAllConfigs().values().forEach(config -> {
                if (!newConfigs.containsKey(config.getName())) {
                    if (!config.getName().equals(et.getDsId()))
                        newConfigs.put(config.getName(), config);
                    else
                        newConfigs.put(config.getName(), et.getDSLinkConfig());
                }
            });
            Path path = Paths.get(LiteStorage.MODEL_PATH, "DATASOURCE.json");
            try {
                Files.write(path, JSON.toJSONBytes(newConfigs));
            } catch (Exception e) {
                throw new BadRequestAlertException("读写文件失败", "DSLinkConfig", path.toString());
            }
        }
        return true;
    }

    @Override
    public void saveBatch(List<DSSetting> list) {
        list.forEach(et->save(et));
    }


    private HashMap<String, DSLink> check=new HashMap<>();
    private Object lock=new Object();

    public DSLink getDataSource(String tag)
    {
        if(StringUtils.isEmpty(tag))
            return DSLink.getDefaultLink();

        if (check.containsKey(tag))
            return check.get(tag);

        DSSetting dstDataSource=null;
        synchronized (lock)
        {
            if (check.containsKey(tag))
                return check.get(tag);
            dstDataSource=this.get(tag);
            if(dstDataSource==null) {
                DSLink defaultLink=DSLink.getDefaultLink();
                check.put(tag,defaultLink);
                return defaultLink;
            }

            if ((!tag.equalsIgnoreCase(dstDataSource.getDsId()))&&check.containsKey(dstDataSource.getDsId()))
                return check.get(dstDataSource.getDsId());
        }
        return buildDS(dstDataSource).getDSLinkConfig();
    }

    public DSSetting buildDS(DSSetting dstDataSource) {
        if(dstDataSource!=null&&(!StringUtils.isEmpty(dstDataSource.getDsCfg())))
        {
            synchronized (lock)
            {
                if(dstDataSource.isDatabase())
                {
                    DataSourceProperty dataSourceProperty = dstDataSource.getDataSourceProperty();
                    DynamicRoutingDataSource ds = dataSource;
                    DataSource dataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);
                    ds.addDataSource(dstDataSource.getDsId(), dataSource);
                }
                check.put(dstDataSource.getDsId(), dstDataSource.getDSLinkConfig());
                if(!dstDataSource.getDsId().equals(dstDataSource.getDsId().toLowerCase()))
                    check.put(dstDataSource.getDsId().toLowerCase(), dstDataSource.getDSLinkConfig());
                if(!ObjectUtils.isEmpty(dstDataSource.getUsings()))
                {
                    dstDataSource.getUsings().forEach(using->{
                        check.put(using, dstDataSource.getDSLinkConfig());
                        if(!using.equals(using.toLowerCase()))
                            check.put(using.toLowerCase(), dstDataSource.getDSLinkConfig());
                    });
                }
            }
        }

        return dstDataSource;
    }

    @Autowired
    @Lazy
    private DynamicRoutingDataSource dataSource;

    @Autowired
    @Lazy
    private DataSourceCreator druidDataSourceCreator;


    /**
     * 查询集合 数据集
     */
    @Override
    public Page<DSSetting> searchDefault(DSSettingSearchContext context) {

        LinkedHashMap<String, DSLink> newConfigs = new LinkedHashMap<>();
        proxy.getAllConfigs().values().forEach(config -> {
            if (!newConfigs.containsKey(config.getName())) {
                newConfigs.put(config.getName(),config);
            }
        });
        List<DSSetting> list=new ArrayList<>();
        newConfigs.values().forEach(config->{
            if((!StringUtils.isEmpty(context.getN_dsid_like())&&(StringUtils.isEmpty(config.getName())||config.getName().indexOf(context.getN_dsid_like())<0)))
                return;
            if((!StringUtils.isEmpty(context.getN_dsname_like())&&(StringUtils.isEmpty(config.getName())||config.getDescription().indexOf(context.getN_dsname_like())<0)))
                return;
            if((!StringUtils.isEmpty(context.getN_dstype_like())&&(StringUtils.isEmpty(config.getName())||config.getType().indexOf(context.getN_dstype_like())<0)))
                return;
            list.add(new DSSetting().setDSLinkConfig(config));
        });
        return new PageImpl<DSSetting>(list,context.getPageable(),list.size());
    }

}


