package org.holer.server.rest;

import java.util.Date;
import java.util.List;
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.HolerResult;
import org.holer.server.util.ServerMgr;
import org.holer.server.util.ServerUtil;
import org.springframework.beans.factory.annotation.Autowired;
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;


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

    @GetMapping({"/{clientId}"})
    public HolerResult<?> findByClientId(@PathVariable("clientId") Long clientId) {
        Optional<HolerClient> optClient = this.clientService.findById(clientId);
        if (!optClient.isPresent()) {
            return ServerUtil.failure(HolerCode.NOT_FOUND);
        }

        HolerClient client = (HolerClient) optClient.get();
        List<HolerPort> ports = this.portService.findByAccessKey(client.getAccessKey());
        if (CollectionUtils.isEmpty(ports)) {
            return ServerUtil.failure(HolerCode.EMPTY);
        }
        return ServerUtil.success(ports);
    }


    /**
     * 增加端口映射
     */
    @PostMapping({"/{accessKey}"})
    public HolerResult<?> addPort(@RequestBody @Valid HolerPort port, @PathVariable("accessKey") String accessKey) {

        if (StringUtils.isEmpty(accessKey)) {//判断秘钥是否为空
            return new HolerResult(HolerCode.BAD_KEY);
        }
        if (null == port.getPortNum() || port.getPortNum() < 1 || port.getPortNum() > 65535) {//判断端口号是否正确
            return new HolerResult(HolerCode.BAD_PUBLIC_PORT);
        }
        if (null != port.getValidDay() && (port.getValidDay() < 0L || port.getValidDay() > 1000L)) { //判断有效期
            return new HolerResult(HolerCode.BAD_DAY);
        }
        if (!ServerUtil.isValid(port.getServer(), "^[0-9a-zA-Z.-:]{3,64}$")) { //判断内网地址是否正确
            return new HolerResult(HolerCode.BAD_SERVER);
        }

//        long usedNum = ServerUtil.getInstance().getPortService().count(); //返回可用实体的数量
//        HolerResult<?> result = ServerUtil.totalNum();//判断是否有购买授权
//        if (usedNum >= result.getTotal()) {
//            if (HolerCode.OK.value() != result.getCode()) {
//                return result;
//            }
//            return new HolerResult(HolerCode.ERR_MAX_MAPPING);
//        }

        HolerClient client = this.clientService.findByAccessKey(accessKey);
        if (null == client) {
            return ServerUtil.failure(HolerCode.NOT_FOUND);//未找到资源
        }

        HolerCode hc = ServerUtil.isValidServer(port.getServer());//判断地址是否正确

        if (StringUtils.isNotEmpty(port.getServer()) && !HolerCode.OK.equals(hc)) {//判断返回结果
            return new HolerResult(hc);
        }

        List<HolerPort> ports = this.portService.findByPortNum(port.getPortNum()); //查询端口是否被使用
        if (CollectionUtils.isNotEmpty(ports)) {
            return new HolerResult(HolerCode.EXIST_PORT);//无法使用的端口
        }

        if (StringUtils.isNotEmpty(port.getDomain())) {
            //判断域名是否可以使用
            if (!ServerUtil.isValid(port.getDomain(), "^[0-9a-zA-Z-.\\s]{3,64}$")) {
                return new HolerResult(HolerCode.BAD_DOMAIN);
            }
            //查询域名是否被使用
            ports = this.portService.findByDomain(port.getDomain());
            if (CollectionUtils.isNotEmpty(ports)) {
                return new HolerResult(HolerCode.EXIST_DOMAIN);
            }
        } else if (HolerType.HTTP.equals(port.getType()) || HolerType.HTTPS.equals(port.getType())) {
            //如果没有域名，并且是web应用，设置默认的域名
            port.setDomain(ServerUtil.defaultDomain(port.getPortNum()));
        }

        if (null == port.getEnabled()) {
            port.setEnabled(Boolean.TRUE);
        }
        if (null == port.getValidDay()) {
            port.setValidDay(10L);
        }

        port.setAccessKey(client.getAccessKey()); //设置key
        port.setCreateAt(new Date());   //设置创建时间
        port.setExpireAt(new Date(System.currentTimeMillis() + port.getValidDay() * 86400000L));

        HolerPort dbPort = (HolerPort) this.portService.save(port); //保存端口映射
        ServerMgr.savePort(dbPort);
        return ServerUtil.success(dbPort);
    }


    /**
     * 修改端口映射
     */
    @PutMapping
    public HolerResult<?> updatePort(@RequestBody HolerPort port) {
        if (null == port.getPortId() || port.getPortId() < 0L) {
            return new HolerResult(HolerCode.BAD_ID);//无效的编号
        }
        if (null != port.getPortNum() && (port.getPortNum() < 1 || port.getPortNum() > 65535)) {
            return new HolerResult(HolerCode.BAD_PUBLIC_PORT); //公网端口无效，有效的端口范围 1-65535
        }
        HolerCode hc = ServerUtil.isValidServer(port.getServer()); //判断地址是否正确
        if (StringUtils.isNotEmpty(port.getServer()) && !HolerCode.OK.equals(hc)) {
            return new HolerResult(hc); //返回错误信息
        }
        //判断域名是否正确
        if (StringUtils.isNotEmpty(port.getDomain()) && !ServerUtil.isValid(port.getDomain(), "^[0-9a-zA-Z-.\\s]{3,64}$")) {
            return new HolerResult(HolerCode.BAD_DOMAIN);
        }
        //判断有效期范围是否正确
        if (null != port.getValidDay() && (port.getValidDay() < 0L || port.getValidDay() > 1000L)) {
            return new HolerResult(HolerCode.BAD_DAY);
        }

        Optional<HolerPort> optPort = this.portService.findById(port.getPortId()); //根据id查询旧的端口映射信息
        if (!optPort.isPresent()) { //没有查询到旧的资源
            return ServerUtil.failure(HolerCode.NOT_FOUND);
        }

        HolerPort dbPort = (HolerPort) optPort.get(); //返回旧的端口映射信息
        if (null != port.getPortNum()) { //端口号不为空
            List<HolerPort> ports = this.portService.findByPortNum(port.getPortNum()); //根据端口号查询该公网端口是否被占用
            if (CollectionUtils.isNotEmpty(ports) && (ports.size() > 1 || !((HolerPort) ports.get(0)).getPortId().equals(port.getPortId()))) {
                return new HolerResult(HolerCode.EXIST_PORT);
            }
            ServerMgr.deletePort(dbPort); //删除原有的端口映射信息
            dbPort.setPortNum(port.getPortNum()); //设置新的端口号
        }
        if (StringUtils.isNotEmpty(port.getDomain())) { //判断用户输入的域名是否为空
            List<HolerPort> ports = this.portService.findByDomain(port.getDomain());  //查询该公网端口是否可用
            if (CollectionUtils.isNotEmpty(ports) && (ports.size() > 1 || !((HolerPort) ports.get(0)).getPortId().equals(port.getPortId()))) {
                return new HolerResult(HolerCode.EXIST_DOMAIN);
            }
            dbPort.setDomain(port.getDomain()); //设置域名
        }
        if (StringUtils.isNotEmpty(port.getServer())) {
            dbPort.setServer(port.getServer());
        }
        if (null != port.getType()) {
            dbPort.setType(port.getType());
        }
        if (null != port.getValidDay()) { //设置过期时间
            dbPort.setExpireAt(new Date(System.currentTimeMillis() + port.getValidDay() * 86400000L));
        }
        if (null != port.getEnabled()) { //是否启用
            dbPort.setEnabled(port.getEnabled());
        }

        dbPort.setCreateAt(new Date());
        dbPort = (HolerPort) this.portService.save(dbPort);
        ServerMgr.savePort(dbPort);
        return ServerUtil.success(dbPort);
    }


    /**
     * 删除指定的端口映射
     */
    @DeleteMapping({"/{portId}"})
    public HolerResult<?> deleteById(@PathVariable("portId") Long portId) {
        if (null == portId || portId < 0L) {
            return new HolerResult(HolerCode.BAD_ID);
        }
        Optional<HolerPort> optPort = this.portService.findById(portId);
        if (!optPort.isPresent()) {
            return ServerUtil.failure(HolerCode.NOT_FOUND);
        }
        HolerPort dbPort = (HolerPort) optPort.get();
        this.portService.deleteById(portId);
        ServerMgr.deletePort(dbPort);
        return ServerUtil.success(portId);
    }
}
