package org.holer.server.rest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.validation.Valid;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.holer.server.constant.HolerCode;
import org.holer.server.constant.HolerType;
import org.holer.server.db.DBClientService;
import org.holer.server.db.DBPortService;
import org.holer.server.model.HolerClient;
import org.holer.server.model.HolerPort;
import org.holer.server.model.HolerReport;
import org.holer.server.model.HolerResult;
import org.holer.server.util.DataCollector;
import org.holer.server.util.ServerMgr;
import org.holer.server.util.ServerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@Validated
@CrossOrigin
@RestController
@RequestMapping({"/api/client"})
public class RESTClientService {
    @Autowired
    private DBClientService clientService;
    @Autowired
    private DBPortService portService;

    /**
     * 查询所有客户端
     */
    @GetMapping({"/all"})
    public HolerResult<?> findAll() {
        List<HolerClient> clients = this.clientService.findAll(new Sort(Sort.Direction.DESC, "clientId"));
        if (CollectionUtils.isEmpty(clients)) {
            return ServerUtil.success(clients);
        }

        List<HolerPort> ports = this.portService.findAll();
        if (CollectionUtils.isEmpty(ports)) {
            return ServerUtil.success(clients); //没有数据，直接返回
        }
        String key = "";
        List<HolerPort> portList = null;
        Map<String, List<HolerPort>> portMap = new HashMap<String, List<HolerPort>>();
        for (HolerPort port : ports) {
            key = port.getAccessKey();
            portList =portMap.get(key);
            if (null == portList) {
                portList = new ArrayList<HolerPort>();
            }
            portList.add(port);
            portMap.put(key, portList);
        }
        for (HolerClient client : clients) {
            key = client.getAccessKey();
            client.setStatus(ServerMgr.clientStatus(key));
            portList = portMap.get(key);
            if (null != portList) {
                client.getPorts().addAll(portList);
            }
        }
        return ServerUtil.success(clients);
    }


    /**
     * 根据key查询客户端信息
     */
    @GetMapping({"/key/{accessKey}"})
    public HolerResult<?> findByAccessKey(@PathVariable("accessKey") String accessKey) {
        HolerClient client = this.clientService.findByAccessKey(accessKey);
        if (null == client) {
            return ServerUtil.failure(HolerCode.EMPTY);
        }
        List<HolerPort> ports = this.portService.findByAccessKey(client.getAccessKey());
        client.getPorts().addAll(ports);
        return ServerUtil.success(client);
    }


    /**
     * 统计信息
     */
    @GetMapping({"/report"})
    public HolerResult<?> getReport() {
        HolerReport report = new HolerReport();
        long totol = this.clientService.count();

        report.setOnlineClient(ServerMgr.countOnlineClient());
        report.setOfflineClient(totol - report.getOnlineClient());

        report.setEnabledClient(this.clientService.countAllByEnabled(Boolean.TRUE));
        report.setDisabledClient(totol - report.getEnabledClient());

        totol = this.portService.count(); //查询端口数

        report.setUpPort(this.portService.countAllByEnabled(Boolean.TRUE));
        report.setDownPort(totol - report.getUpPort());

        report.setExpiredPort(this.portService.countExpiredPort());
        report.setUnexpiredPort(totol - report.getExpiredPort());

        report.setActivePort(this.portService.countActivePort());
        report.setInactivePort(totol - report.getActivePort());

        Map<HolerType, Long> appType = new HashMap<HolerType, Long>();
        for (HolerType type : HolerType.values()) {
            appType.put(type, this.portService.countAllByType(type));
        }
        report.setAppType(appType);
        report.setTraffic(DataCollector.getAllData());
        return ServerUtil.success(report);
    }


    @PostMapping
    public HolerResult<?> addClient(@RequestBody @Valid HolerClient client) {
        String key = client.getAccessKey();
        if (StringUtils.isBlank(key)) {
            client.setAccessKey(ServerUtil.randomID());
        } else if (!ServerUtil.isValid(key, "^[0-9a-zA-Z_-]{16,64}$")) {
            return ServerUtil.failure(HolerCode.BAD_KEY);
        }
        if (!ServerUtil.isValid(client.getName(), "^[0-9a-zA-Z.@-]{6,64}$")) {
            return ServerUtil.failure(HolerCode.BAD_EMAIL);
        }
        HolerClient dbClient = this.clientService.findByAccessKey(key);
        if (null != dbClient) {
            return ServerUtil.failure(HolerCode.EXIST_KEY);
        }
        if (null == client.getStatus()) {
            client.setStatus(0);
        }
        if (null == client.getEnabled()) {
            client.setEnabled(Boolean.TRUE);
        }
        dbClient = (HolerClient) this.clientService.save(client);
        ServerMgr.saveClient(dbClient);
        return ServerUtil.success(dbClient);
    }


    /**
     * 更新客户信息
     */
    @PutMapping
    public HolerResult<?> updateClient(@RequestBody @Valid HolerClient client) {
        if (null == client.getClientId() || client.getClientId() < 0L) {
            return new HolerResult(HolerCode.BAD_ID);
        }
        Optional<HolerClient> optClient = this.clientService.findById(client.getClientId());
        if (!optClient.isPresent()) {
            return ServerUtil.failure(HolerCode.NOT_FOUND);
        }
        HolerClient dbClient = (HolerClient) optClient.get();
        if (StringUtils.isNotBlank(client.getAccessKey())) {
            if (!ServerUtil.isValid(client.getAccessKey(), "^[0-9a-zA-Z_-]{16,64}$")) {
                return ServerUtil.failure(HolerCode.BAD_KEY);
            }
            HolerClient hclient = this.clientService.findByAccessKey(client.getAccessKey());
            if (null != hclient && !hclient.getClientId().equals(client.getClientId())) {
                return ServerUtil.failure(HolerCode.EXIST_KEY);
            }
            dbClient.setAccessKey(client.getAccessKey());
        }
        if (StringUtils.isNotBlank(client.getName())) {
            if (!ServerUtil.isValid(client.getName(), "^[0-9a-zA-Z.@-]{6,64}$")) {
                return ServerUtil.failure(HolerCode.BAD_EMAIL);
            }
            dbClient.setName(client.getName());
        }
        if (null != client.getEnabled()) {
            dbClient.setEnabled(client.getEnabled());
        }
        if (null != client.getStatus()) {
            dbClient.setStatus(client.getStatus());
        }
        dbClient = (HolerClient) this.clientService.save(dbClient);
        ServerMgr.saveClient(dbClient);
        List<HolerPort> allHolerPort = this.portService.findAll(); //根据id查询旧的端口映射信息
        if (allHolerPort!=null){
            //更新用户信息时，如果修改了秘钥，需要更新其对应的映射端口
            for (HolerPort holerport:allHolerPort) {
                if (!holerport.getAccessKey().equals(client.getAccessKey())){
                    holerport.setAccessKey(client.getAccessKey());
                    this.portService.updateAccessKey(client.getAccessKey(),holerport.getPortId());
                    ServerMgr.savePort(holerport);
                }
            }
        }
        return ServerUtil.success(dbClient);
    }


    /**
     * 根据客户ID删除客户信息
     */
    @DeleteMapping({"/{clientId}"})
    @Transactional
    public HolerResult<?> deleteById(@PathVariable("clientId") Long clientId) {
        if (null == clientId || clientId < 0L) {
            return new HolerResult(HolerCode.BAD_ID);
        }
        Optional<HolerClient> optClient = this.clientService.findById(clientId);
        if (!optClient.isPresent()) {
            return ServerUtil.failure(HolerCode.NOT_FOUND);
        }
        HolerClient dbClient = (HolerClient) optClient.get();
        this.portService.deleteByAccessKey(dbClient.getAccessKey());
        this.clientService.deleteById(clientId);
        ServerMgr.deleteClient(dbClient);
        return ServerUtil.success(clientId);
    }
}
