package com.zcloud.personnel.positioning.command;

import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRuleDO;
import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO;
import com.zcloud.personnel.positioning.persistence.dataobject.TerminalBindDO;
import com.zcloud.personnel.positioning.persistence.dataobject.TerminalDO;
import com.zcloud.personnel.positioning.persistence.repository.FindsSyncStateRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class FindsSyncStateService {
    private static final String FENCE = "FENCE";
    private static final String ALARM_RULE = "ALARM_RULE";
    private static final String TERMINAL = "TERMINAL";
    private static final String TERMINAL_BIND = "TERMINAL_BIND";

    private final FindsSyncStateRepository repository;

    public boolean completeFence(FenceDO snapshot, Long findsId, String findsCode, String rawJson) {
        Map<String, Object> params = stateParams(snapshot.getId(), snapshot.getSyncToken(), false);
        params.put("findsId", findsId);
        params.put("findsCode", findsCode);
        params.put("rawJson", rawJson);
        int updated = repository.complete(FENCE, params);
        if (updated == 0) {
            recordFenceMapping(snapshot.getId(), findsId, findsCode);
        }
        return updated > 0;
    }

    public boolean completeAlarmRule(AlarmRuleDO snapshot, Long findsId, String rawJson) {
        int updated = complete(ALARM_RULE, snapshot.getId(), snapshot.getSyncToken(), findsId, rawJson);
        if (updated == 0) {
            recordAlarmRuleMapping(snapshot.getId(), findsId);
        }
        return updated > 0;
    }

    public boolean completeTerminal(TerminalDO snapshot, Long findsId, String rawJson) {
        int updated = complete(TERMINAL, snapshot.getId(), snapshot.getSyncToken(), findsId, rawJson);
        if (updated == 0) {
            recordTerminalMapping(snapshot.getId(), findsId);
        }
        return updated > 0;
    }

    public boolean completeTerminalBinding(TerminalBindDO snapshot, Long findsTerminalId) {
        return complete(TERMINAL_BIND, snapshot.getId(), snapshot.getSyncToken(), findsTerminalId, null) > 0;
    }

    public void failFence(FenceDO snapshot, boolean deleting, String rawJson) {
        fail(FENCE, snapshot.getId(), snapshot.getSyncToken(), deleting, rawJson);
    }

    public void failAlarmRule(AlarmRuleDO snapshot, boolean deleting, String rawJson) {
        fail(ALARM_RULE, snapshot.getId(), snapshot.getSyncToken(), deleting, rawJson);
    }

    public void failTerminal(TerminalDO snapshot, boolean deleting, String rawJson) {
        fail(TERMINAL, snapshot.getId(), snapshot.getSyncToken(), deleting, rawJson);
    }

    public void failTerminalBinding(TerminalBindDO snapshot) {
        fail(TERMINAL_BIND, snapshot.getId(), snapshot.getSyncToken(), false, null);
    }

    public void recordFenceMapping(Long id, Long findsId, String findsCode) {
        if (id == null || findsId == null) {
            return;
        }
        Map<String, Object> params = mappingParams(id, findsId);
        params.put("findsCode", findsCode);
        repository.recordMapping(FENCE, params);
    }

    public void recordAlarmRuleMapping(Long id, Long findsId) {
        recordMapping(ALARM_RULE, id, findsId);
    }

    public void recordTerminalMapping(Long id, Long findsId) {
        recordMapping(TERMINAL, id, findsId);
    }

    public boolean clearFenceMapping(FenceDO snapshot, Long expectedFindsId) {
        return snapshot != null && clearMapping(FENCE, snapshot.getId(), snapshot.getSyncToken(), expectedFindsId);
    }

    public boolean clearAlarmRuleMapping(AlarmRuleDO snapshot, Long expectedFindsId) {
        return snapshot != null && clearMapping(ALARM_RULE, snapshot.getId(), snapshot.getSyncToken(), expectedFindsId);
    }

    public boolean clearTerminalMapping(TerminalDO snapshot, Long expectedFindsId) {
        return snapshot != null && clearMapping(TERMINAL, snapshot.getId(), snapshot.getSyncToken(), expectedFindsId);
    }

    public boolean isCurrentFence(FenceDO snapshot) {
        return snapshot != null && isCurrent(FENCE, snapshot.getId(), snapshot.getSyncToken());
    }

    public boolean isCurrentAlarmRule(AlarmRuleDO snapshot) {
        return snapshot != null && isCurrent(ALARM_RULE, snapshot.getId(), snapshot.getSyncToken());
    }

    public boolean isCurrentTerminal(TerminalDO snapshot) {
        return snapshot != null && isCurrent(TERMINAL, snapshot.getId(), snapshot.getSyncToken());
    }

    public void recordAreaFenceMapping(Long areaId, Long fenceId, Long findsFenceId) {
        if (areaId == null || findsFenceId == null) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        params.put("areaId", areaId);
        params.put("fenceId", fenceId);
        params.put("findsFenceId", findsFenceId);
        repository.recordAreaFenceMapping(params);
    }

    public boolean deleteFenceIfCurrent(FenceDO snapshot) {
        return deleteCurrent(FENCE, snapshot.getId(), snapshot.getSyncToken());
    }

    public boolean deleteAlarmRuleIfCurrent(AlarmRuleDO snapshot) {
        return deleteCurrent(ALARM_RULE, snapshot.getId(), snapshot.getSyncToken());
    }

    public boolean deleteTerminalIfCurrent(TerminalDO snapshot) {
        return deleteCurrent(TERMINAL, snapshot.getId(), snapshot.getSyncToken());
    }

    private int complete(String type, Long id, String token, Long findsId, String rawJson) {
        Map<String, Object> params = stateParams(id, token, false);
        params.put("findsId", findsId);
        params.put("rawJson", rawJson);
        return repository.complete(type, params);
    }

    private void fail(String type, Long id, String token, boolean deleting, String rawJson) {
        Map<String, Object> params = stateParams(id, token, deleting);
        params.put("status", deleting ? FindsPendingSyncService.STATUS_DELETE_FAIL
                : FindsPendingSyncService.STATUS_FAIL);
        params.put("rawJson", rawJson);
        repository.fail(type, params);
    }

    private void recordMapping(String type, Long id, Long findsId) {
        if (id != null && findsId != null) {
            repository.recordMapping(type, mappingParams(id, findsId));
        }
    }

    private boolean clearMapping(String type, Long id, String token, Long findsId) {
        if (id == null || findsId == null) {
            return false;
        }
        Map<String, Object> params = stateParams(id, token, false);
        params.put("findsId", findsId);
        return repository.clearMapping(type, params) > 0;
    }

    private boolean isCurrent(String type, Long id, String token) {
        return id != null && repository.isCurrent(type, stateParams(id, token, false));
    }

    private boolean deleteCurrent(String type, Long id, String token) {
        return repository.deleteCurrent(type, stateParams(id, token, true)) > 0;
    }

    private Map<String, Object> stateParams(Long id, String token, boolean deleting) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", id);
        params.put("token", token);
        params.put("deleting", deleting);
        params.put("syncTime", LocalDateTime.now());
        return params;
    }

    private Map<String, Object> mappingParams(Long id, Long findsId) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", id);
        params.put("findsId", findsId);
        return params;
    }
}
