package org.zstack.network.service.virtualrouter.dns;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.timeout.ApiTimeoutManager;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.message.MessageReply;
import org.zstack.header.network.l3.L3NetworkDnsVO;
import org.zstack.header.network.l3.L3NetworkDnsVO_;
import org.zstack.header.network.service.NetworkServiceType;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.network.service.virtualrouter.*;
import org.zstack.network.service.virtualrouter.VirtualRouterCommands.DnsInfo;
import org.zstack.network.service.virtualrouter.VirtualRouterCommands.SetDnsCmd;
import org.zstack.network.service.virtualrouter.VirtualRouterCommands.SetDnsRsp;
import org.zstack.utils.Utils;
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;

import static org.zstack.core.Platform.operr;

import java.util.*;

@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VirtualRouterSyncDnsOnStartFlow extends NoRollbackFlow {
    private static final CLogger logger = Utils.getLogger(VirtualRouterSyncDnsOnStartFlow.class);

    @Autowired
    private CloudBus bus;
	@Autowired
	private VirtualRouterManager vrMgr;
	@Autowired
	private DatabaseFacade dbf;
    @Autowired
    private ErrorFacade errf;
    @Autowired
    private ApiTimeoutManager apiTimeoutManager;

    @Override
    public void run(final FlowTrigger chain, final Map data) {
        final VirtualRouterVmInventory vr = (VirtualRouterVmInventory) data.get(VirtualRouterConstant.Param.VR.toString());

        List<String> nwServed = vr.getGuestL3Networks();
        if (nwServed == null || nwServed.isEmpty()) {
            chain.next();
            return;
        }
        List<String> l3Uuids = vrMgr.selectL3NetworksNeedingSpecificNetworkService(nwServed, NetworkServiceType.DNS);
        if (l3Uuids.isEmpty()) {
            chain.next();
            return;
        }

        if (VirtualRouterSystemTags.DEDICATED_ROLE_VR.hasTag(vr.getUuid()) && !VirtualRouterSystemTags.VR_DNS_ROLE.hasTag(vr.getUuid())) {
            chain.next();
            return;
        }

        new VirtualRouterRoleManager().makeDnsRole(vr.getUuid());

        SimpleQuery<L3NetworkDnsVO> query = dbf.createQuery(L3NetworkDnsVO.class);
        query.select(L3NetworkDnsVO_.dns);
        query.add(L3NetworkDnsVO_.l3NetworkUuid, Op.IN, l3Uuids);
        List<String> lst = query.listValue();
        if (lst.isEmpty()) {
            chain.next();
            return;
        }

        Set<String> dnsAddresses = new HashSet<String>(lst.size());
        dnsAddresses.addAll(lst);

        final List<DnsInfo> dns = new ArrayList<DnsInfo>(dnsAddresses.size());
        for (String d : dnsAddresses) {
            DnsInfo dinfo = new DnsInfo();
            dinfo.setDnsAddress(d);
            dinfo.setNicMac(vr.getGuestNics().get(0).getMac());
            dns.add(dinfo);
        }

        SetDnsCmd cmd = new SetDnsCmd();
        cmd.setDns(dns);

        VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg();
        msg.setVmInstanceUuid(vr.getUuid());
        msg.setPath(VirtualRouterConstant.VR_SET_DNS_PATH);
        msg.setCommand(cmd);
        msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m"));
        bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vr.getUuid());
        bus.send(msg, new CloudBusCallBack(chain) {
            @Override
            public void run(MessageReply reply) {
                if (!reply.isSuccess()) {
                    chain.fail(reply.getError());
                    return;
                }

                VirtualRouterAsyncHttpCallReply re = reply.castReply();
                SetDnsRsp ret = re.toResponse(SetDnsRsp.class);
                if (ret.isSuccess()) {
                    chain.next();
                } else {
                    ErrorCode err = operr("virtual router[name: %s, uuid: %s] failed to configure dns%s, %s ",
                            vr.getName(), vr.getUuid(), JSONObjectUtil.toJsonString(dns), ret.getError());
                    chain.fail(err);
                }
            }
        });
    }
}

@@ -365,32 +365,61 @@
 DatabaseFacade;%0A
+import org.zstack.core.db.Q;%0A
 import org.zstac
@@ -2997,226 +2997,119 @@
 ist%3C
-String%3E lst = query.listValue();%0A        if (lst.isEmpty()) %7B%0A            chain.next();%0A            return;%0A        %7D%0A%0A        Set%3CString%3E dnsAddresses = new HashSet%3CString%3E(lst.size());%0A        dnsAddresses.addAll(l
+L3NetworkDnsVO%3E l3NetworkDnsVOS =  Q.New(L3NetworkDnsVO.class).in(L3NetworkDnsVO_.l3NetworkUuid, l3Uuids).li
 st
+(
 );%0A%0A
@@ -3165,28 +3165,31 @@
 nsInfo%3E(
-dnsAddresses
+l3NetworkDnsVOS
 .size())
@@ -3207,31 +3207,43 @@
 or (
-String d : dnsAddresses
+L3NetworkDnsVO vo : l3NetworkDnsVOS
 ) %7B%0A
@@ -3317,17 +3317,27 @@
 Address(
-d
+vo.getDns()
 );%0A     
@@ -3380,53 +3380,356 @@
 cs()
-.get(0).getMac());%0A            dns.add(dinfo)
+%0A                    .stream()%0A                    .filter(nic -%3E nic.getL3NetworkUuid().equals(vo.getL3NetworkUuid()))%0A                    .findFirst().get()%0A                    .getMac());%0A            dns.add(dinfo);%0A        %7D%0A        List%3CString%3E lst = query.listValue();%0A        if (lst.isEmpty()) %7B%0A            chain.next();%0A            return
 ;%0A  

Allow multiple dns setting