package id.iptek.utms.agent.worker;

import com.google.gson.Gson;
import id.iptek.utms.agent.GenericCache;
import id.iptek.utms.agent.dao.DownloadTaskDao;
import id.iptek.utms.agent.dao.TerminalDao;
import id.iptek.utms.agent.model.TaskAckReq;
import id.iptek.utms.agent.model.TerminalIdObj;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;

/**
 *
 * @author Jaka
 */
public class DownloadTaskAckWorker extends Worker {

    private DownloadTaskDao dao = new DownloadTaskDao();
    private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private TerminalDao terminalDao = new TerminalDao();
    
    public DownloadTaskAckWorker() {
    }

    @Override
    public void messageArrived(String string, MqttMessage mm) throws Exception {
        try {
            String json = new String(mm.getPayload());
            logger.debug("<< " + json + " (" + mm.getId() + " - " + mm.getQos() + ")");
            Gson gson = new Gson();
            Map map = gson.fromJson(json, Map.class);
            
            String reqId = UUID.randomUUID().toString();
            String requestType = (String) map.get("req_type");
            if(!"DOWNLOAD_ACK".equals(requestType)) {
                return;
            }
            
            String terminalSN = (String) map.get("device_sn");
            String ackId = (String) map.get("ack_id");
            String status = (String) map.get("status");
            if("ACCEPTED".equals(status)) {
                status = "NOT_STARTED";
            }
            int activity = translateActivityFromStatus(status);
            
            TaskAckReq req = new TaskAckReq();
            req.setReqId(reqId);
            req.setAckId(ackId);
            req.setTerminalSN(terminalSN);
            req.setActivity(activity);
            if(ackId.startsWith("BROADCAST_")) {
                String taskId = ackId.substring(10);
                req.setBroadcasted(true);
                req.setTaskId(taskId);
            }
            // get terminal id from cache
            String terminalId = getTerminalIdBySN(terminalSN);
            if(terminalId != null) {
                req.setTerminalId(terminalId);
                boolean saved = dao.save(req);
                logger.debug("Download task ACK request saved? {}", saved);
                if(saved) {
                    if("INSTALL_SUCCESS".equals(status) ||
                        "INSTALL_FAIL".equals(status)) {
                        // send ack DOWNLOAD_TASK_CONFIRM_ACK
                        broadcastDownloadTasksConfirmACK(mqttClient, terminalSN, ackId);
                    }
                }
            } else {
                logger.warn("Invalid SN: {}", terminalSN);
            }
        } catch(Exception ex) {
            logger.error("Download task ACK: {}", ex.getMessage(), ex);
        }
    }
    
    private int translateActivityFromStatus(String status) {
        int activity = 0; // initial
        switch(status) {
            case "INITIAL":
                activity = 0;
                break;
            case "NOT_STARTED":
                activity = 1;
                break;
            case "DOWNLOAD_STARTED":
                activity = 2;
                break;
            case "DOWNLOAD_SUCCESS":
                activity = 3;
                break;
            case "DOWNLOAD_FAIL":
                activity = 4;
                break;
            case "INSTALL_STARTED":
                activity = 5;
                break;
            case "INSTALL_SUCCESS":
                activity = 6;
                break;
            case "INSTALL_FAIL":
                activity = 7;
                break;
            case "ACCEPTED":
                activity = 8;
                break;
        }
        return activity;
    }
    
    // send download task to device topic via mqtt
    public void broadcastDownloadTasksConfirmACK(MqttClient mqttClient,
                                               String terminalSN,
                                               String ackId) throws Exception {
        logger.debug("Broadcast tasks ...");
        try {
            try {
                int qos = 2;
                String topicName = terminalSN.toUpperCase() + "_IN";
                // generate json
                Gson gson = new Gson();
                Map messageMap = new HashMap<>();
                messageMap.put("req_id", UUID.randomUUID().toString());
                messageMap.put("req_time", dateTimeFormat.format(new Date()));
                // we'll use log's id as ack_id
                messageMap.put("ack_id", ackId);
                messageMap.put("req_type", "DOWNLOAD_TASK_CONFIRM_ACK");

                String messageData = gson.toJson(messageMap);

                logger.debug("Try publish message to: {}", topicName);
                mqttClient.publish(topicName, messageData.getBytes(), qos, false);
                logger.debug("Message published!");
            } catch(Exception ex) {
                logger.error("Error publish to device: {}", ex.getMessage(), ex);
            } finally {
            }
        } finally {
            logger.debug("Start broadcast download tasks DONE");
        }
    }
    
    private String getTerminalIdBySN(String sn) throws Exception {
        try {
            GenericCache cache = GenericCache.getCache("terminalId");
            String terminalId = (String) cache.get(sn.toLowerCase());
            if(terminalId == null) {
                // load from db first
                TerminalIdObj terminalObj = terminalDao.getTerminalIdBySN(sn);
                if(terminalObj != null) {
                    terminalId = terminalObj.getTerminalId();
                    cache.put(sn.toLowerCase(), terminalId);
                }
            }
            return terminalId;
        } finally {}
    }
}
