package com.ucar.datalink.biz.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Sets;
import com.ucar.datalink.common.errors.TaskConflictException;
import com.ucar.datalink.domain.task.TaskStatus;
import com.ucar.datalink.biz.service.TaskStatusService;
import com.ucar.datalink.common.zookeeper.DLinkZkPathDef;
import com.ucar.datalink.common.zookeeper.DLinkZkUtils;
import org.I0Itec.zkclient.DataUpdater;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.apache.zookeeper.CreateMode;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 任务状态管理服务类,基于Zookeeper.
 * <p>
 * Created by lubiao on 2016/12/6.
 */
@Service
public class TaskStatusServiceImpl implements TaskStatusService {

    @Override
    public void addStatus(TaskStatus status) throws TaskConflictException {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        String statusPath = DLinkZkPathDef.getTaskStatusNode(status.getId());

        byte[] bytes = JSON.toJSONBytes(status);
        try {
            zkUtils.zkClient().createPersistent(DLinkZkPathDef.getTaskNode(status.getId()), true);
            zkUtils.zkClient().create(statusPath, bytes, CreateMode.EPHEMERAL);
        } catch (ZkNodeExistsException e) {
            byte[] data = zkUtils.zkClient().readData(statusPath, true);
            if (data != null) {
                TaskStatus otherTaskStatus = JSON.parseObject(data, TaskStatus.class);
                throw new TaskConflictException(status.getId(), status.getWorkerId(), otherTaskStatus.getWorkerId(),
                        status.getExecutionId(), otherTaskStatus.getExecutionId());
            } else {
                addStatus(status);
            }
        }
    }

    @Override
    public void updateStatus(TaskStatus status) {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        String statusPath = DLinkZkPathDef.getTaskStatusNode(status.getId());
        byte[] bytes = JSON.toJSONBytes(status);

        zkUtils.zkClient().updateDataSerialized(statusPath, new DataUpdater<byte[]>() {

            @Override
            public byte[] update(byte[] currentData) {
                return bytes;
            }
        });
    }

    @Override
    public void removeStatus(String taskId) {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        String statusPath = DLinkZkPathDef.getTaskStatusNode(taskId);
        zkUtils.zkClient().delete(statusPath);
    }

    @Override
    public Collection<TaskStatus> getAll() {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        List<TaskStatus> result = new ArrayList<>();
        for (String taskId : tasks()) {
            byte[] bytes = zkUtils.zkClient().readData(DLinkZkPathDef.getTaskStatusNode(taskId), true);
            if (bytes != null) {
                result.add(JSON.parseObject(bytes, TaskStatus.class));
            }
        }
        return result;
    }

    @Override
    public TaskStatus getStatus(String taskId) {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        byte[] bytes = zkUtils.zkClient().readData(DLinkZkPathDef.getTaskStatusNode(taskId), true);
        if (bytes != null) {
            return JSON.parseObject(bytes, TaskStatus.class);
        } else {
            return null;
        }
    }

    @Override
    public Set<String> tasks() {
        DLinkZkUtils zkUtils = DLinkZkUtils.get();
        try {
            List<String> list = zkUtils.zkClient().getChildren(DLinkZkPathDef.TaskRoot);
            return list == null ? Sets.newHashSet() : list.stream().collect(Collectors.toSet());
        } catch (ZkNoNodeException e) {
            return Sets.newHashSet();
        }

    }
}