package com.ruoyi.file.object;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public final class StateMachine
{
    public long uploadSize = 0; // 上传字节数
    public long writeSize = 0; // 写入字节数
    public long numUpload = 0; // 上传文件数
    public long numWrite = 0; // 写入文件数
    public long numError = 0; // 处理错误数
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastUploadTime; // 最近上传时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastWriteTime; // 最近写入时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastErrorTime; // 最近错误时间
    public long maxUploadSize = 0; // 最大上传字节数
    public long maxWriteSize = 0; // 最大写入字节数
    public long maxHandleDuration = 0; // 最长处理时间

    public long gcCount; // GC次数
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastGCTime; // 最近GC时间

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date startTime = new Date(); // 本次开始时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastEndTime; // 上次结束时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    public Date lastDumpTime; // 上次dump时间

    private final Map<String, ProjectState> stateMap = new LinkedHashMap<>();
    public static final StateMachine        INSTANCE = new StateMachine();

    public synchronized void AddState(ProjectState state)
    {
        ProjectState projectState = stateMap.get(state.token);
        if(null != projectState)
            projectState.Sum(state);
        else
            stateMap.put(state.token, state);
        Sum(state);
    }

    private void Sum(ProjectState state)
    {
        this.uploadSize += state.uploadSize;
        this.writeSize += state.writeSize;
        this.numUpload += state.numUpload;
        this.numWrite += state.numWrite;
        this.numError += state.numError;
        if(null == this.lastUploadTime || state.lastUploadTime.after(this.lastUploadTime))
            this.lastUploadTime = state.lastUploadTime;
        if(null == this.lastWriteTime || state.lastWriteTime.after(this.lastWriteTime))
            this.lastWriteTime = state.lastWriteTime;
        if(null == this.lastErrorTime || state.lastErrorTime.after(this.lastErrorTime))
            this.lastErrorTime = state.lastErrorTime;
        if(state.maxUploadSize > this.maxUploadSize)
            this.maxUploadSize = state.maxUploadSize;
        if(state.maxWriteSize > this.maxWriteSize)
            this.maxWriteSize = state.maxWriteSize;
        if(state.maxHandleDuration > this.maxHandleDuration)
            this.maxHandleDuration = state.maxHandleDuration;
    }

    private ProjectState State(String token)
    {
        ProjectState state = stateMap.get(token);
        if(null == state)
        {
            state = new ProjectState(token);
            stateMap.put(token, state);
        }
        return state;
    }

    public void AddUploadSize(String token, long uploadSize)
    {
        this.uploadSize += uploadSize;
        State(token).AddUploadSize(uploadSize);
    }

    public void AddWriteSize(String token, long writeSize)
    {
        this.writeSize += writeSize;
        State(token).AddWriteSize(writeSize);
    }

    public void AddNumUpload(String token, long numUpload)
    {
        this.numUpload += numUpload;
        State(token).AddNumUpload(numUpload);
    }

    public void AddNumWrite(String token, long numWrite)
    {
        this.numWrite += numWrite;
        State(token).AddNumWrite(numWrite);
    }

    public void AddNumError(String token, long numError)
    {
        this.numError += numError;
        State(token).AddNumError(numError);
    }

    public void SetLastUploadTime(String token, Date lastUploadTime)
    {
        this.lastUploadTime = lastUploadTime;
        State(token).SetLastUploadTime(lastUploadTime);
    }

    public void SetLastWriteTime(String token, Date lastWriteTime)
    {
        this.lastWriteTime = lastWriteTime;
        State(token).SetLastWriteTime(lastWriteTime);
    }

    public void SetLastErrorTime(String token, Date lastErrorTime)
    {
        this.lastErrorTime = lastErrorTime;
        State(token).SetLastErrorTime(lastErrorTime);
    }

    public void SetMaxUploadSize(String token, long maxUploadSize)
    {
        if(maxUploadSize > this.maxUploadSize)
            this.maxUploadSize = maxUploadSize;
        State(token).SetMaxUploadSize(maxUploadSize);
    }

    public void SetMaxWriteSize(String token, long maxWriteSize)
    {
        if(maxWriteSize > this.maxWriteSize)
            this.maxWriteSize += maxWriteSize;
        State(token).SetMaxWriteSize(maxWriteSize);
    }

    public void SetMaxHandleDuration(String token, long maxHandleDuration)
    {
        if(maxHandleDuration > this.maxHandleDuration)
            this.maxHandleDuration += maxHandleDuration;
        State(token).SetMaxHandleDuration(maxHandleDuration);
    }

    public void IncrUploadSize(String token, long uploadSize)
    {
        this.uploadSize += uploadSize;
        State(token).AddUploadSize(uploadSize);
    }

    public void IncrNumUpload(String token)
    {
        this.numUpload++;
        State(token).AddNumUpload(1);
    }

    public void IncrNumWrite(String token)
    {
        this.numWrite++;
        State(token).AddNumWrite(1);
    }

    public void IncrNumError(String token)
    {
        this.numError++;
        State(token).AddNumError(1);
    }

    public void GC()
    {
        //System.gc();
        gcCount++;
        lastGCTime = new Date();
    }

    public ProjectState GetState(String token)
    {
        return stateMap.get(token);
    }

    public List<ProjectState> GetStates()
    {
        return new ArrayList<>(stateMap.values());
    }

    public void FromMap(Map<?, ?> parms)
    {
        CopyOptions options = CopyOptions.create();
        options.setIgnoreProperties("startTime", "gcCount", "lastGCTime");
        options.setIgnoreError(true);
        BeanUtil.fillBeanWithMap(parms, this, options);
        Object obj = parms.get("stateMap");
        if(obj instanceof Map)
        {
            Map<?, ?> m = (Map<?, ?>) obj;
            m.forEach((k, v) -> {
                ProjectState s = ProjectState.FromMap((Map<?, ?>)v);
                stateMap.put(s.token, s);
            });
        }
    }

    public Map<?, ?> ToMap()
    {
        Map<String, Object> map = BeanUtil.beanToMap(this);
        map.put("stateMap", stateMap);
        return map;
    }

    public void Finish()
    {
        Save();
        lastEndTime = new Date();
    }

    public void Save()
    {
        lastDumpTime = new Date();
    }

    public String ToString(String split)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("上传文件大小: ").append(uploadSize);
        sb.append(split);
        sb.append("写入文件大小: ").append(writeSize);
        sb.append(split);
        sb.append("上传文件数: ").append(numUpload);
        sb.append(split);
        sb.append("写入文件数: ").append(numWrite);
        sb.append(split);
        sb.append("上传错误数: ").append(numError);
        sb.append(split);
        sb.append("最近上传时间: ").append(lastUploadTime);
        sb.append(split);
        sb.append("最近写入时间: ").append(lastWriteTime);
        sb.append(split);
        sb.append("最近错误时间: ").append(lastErrorTime);
        sb.append(split);
        sb.append("最大上传文件大小: ").append(maxUploadSize);
        sb.append(split);
        sb.append("最大写入文件大小: ").append(maxWriteSize);
        sb.append(split);
        sb.append("最大处理间隔: ").append(maxHandleDuration);
        sb.append(split);
        sb.append("GC次数: ").append(gcCount);
        sb.append(split);
        sb.append("最近GC时间: ").append(lastGCTime);
        sb.append(split);
        sb.append("本次开始时间: ").append(startTime);
        sb.append(split);
        sb.append("上次结束时间: ").append(lastEndTime);
        sb.append(split);
        sb.append("上次dump时间: ").append(lastDumpTime);
        sb.append(split);
        sb.append("记录数: ").append(stateMap.size());
        return sb.toString();
    }

    private StateMachine() {}
}
