package ru.yandex.webmaster3.storage.logging;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.http.WebmasterJsonModule;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseException;
import ru.yandex.webmaster3.storage.util.fs.persistence.CHBatchWriter;

/**
 * @author tsyplyaev
 */
public class TasksLoggingBatchWriter extends CHBatchWriter<TaskLogEntry> {
    private static final ObjectMapper OM = new ObjectMapper()
            .registerModules(new JodaModule(), new WebmasterJsonModule(false))
            .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    private static final Charset CHARSET = StandardCharsets.UTF_8;
    private static final int INTERVAL_MIN = 2;

    @Autowired
    private TasksLoggingCHDao mdbPeriodicTasksLoggingCHDao;

    public TasksLoggingBatchWriter() {
        super(INTERVAL_MIN, 400_000);
    }

    @Override
    public void logSyncInternal(List<TaskLogEntry> items) {
        try {
            mdbPeriodicTasksLoggingCHDao.addEntries(items);
        } catch (ClickhouseException | JsonProcessingException e) {
            throw new WebmasterException("Sync log failed",
                    new WebmasterErrorResponse.ClickhouseErrorResponse(getClass(), null), e);
        }
    }

    public void addEntry(DateTime date, String taskType, TaskLogEntry.EventType eventType, String hostName,
                         UUID runId, Long runTime, Object state, Object error, Object ydbTrace) throws JsonProcessingException {

        ObjectNode stateJson = null;
        if (state != null || ydbTrace != null) {
            stateJson = state == null ? OM.createObjectNode() : OM.valueToTree(state);

            stateJson.set("_ydbTrace", OM.valueToTree(ydbTrace));
        }
        write(new TaskLogEntry(
                date, taskType, eventType, hostName, runId,
                runTime,
                stateJson,
                OM.valueToTree(error)));
    }

    @Override
    protected byte[] serialize(TaskLogEntry entity) {
        try {
            return OM.writeValueAsBytes(entity);
        } catch (JsonProcessingException e) {
            throw new WebmasterException("Failed to serialize entity",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), null), e);
        }
    }

    @Override
    protected TaskLogEntry deserialize(byte[] data, int offset, int len) {
        try {
            return OM.readValue(data, offset, len, TaskLogEntry.class);
        } catch (IOException e) {
            throw new WebmasterException("Failed to deserialize entity from string " + new String(data, CHARSET),
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), null), e);
        }
    }
}
