package com.zcloud.zcGbsServicer.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.zcloud.zcGbsServicer.api.AlarmRecordServiceI;
import com.zcloud.zcGbsServicer.command.convertor.AlarmRecordCoConvertor;
import com.zcloud.zcGbsServicer.dto.AlarmRecordAddCmd;
import com.zcloud.zcGbsServicer.dto.AlarmRecordClearCmd;
import com.zcloud.zcGbsServicer.dto.AlarmRecordHandleCmd;
import com.zcloud.zcGbsServicer.dto.AlarmRecordPageQry;
import com.zcloud.zcGbsServicer.dto.clientobject.AlarmRecordCO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireAlarmInfoDO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireBrigadeDO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireBrigadeUserDO;
import com.zcloud.zcGbsServicer.persistence.repository.FireAlarmInfoRepository;
import com.zcloud.zcGbsServicer.persistence.repository.FireBrigadeRepository;
import com.zcloud.zcGbsServicer.persistence.repository.FireBrigadeUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AlarmRecordServiceImpl extends FireAlarmServiceSupport implements AlarmRecordServiceI {

    private static final int ALARM_MODE_PHONE = 1;
    private static final int ALARM_MODE_APP = 2;
    private static final int ALARM_STATUS_ALARMING = 1;
    private static final int ALARM_STATUS_DISPOSING = 2;
    private static final int ALARM_STATUS_CLEARED = 3;
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final FireAlarmInfoRepository fireAlarmInfoRepository;
    private final FireBrigadeRepository fireBrigadeRepository;
    private final FireBrigadeUserRepository fireBrigadeUserRepository;
    private final AlarmRecordCoConvertor alarmRecordCoConvertor;

    @Override
    public PageResponse<AlarmRecordCO> listPage(AlarmRecordPageQry qry) {
        Map<String, Object> params = new HashMap<>();
        params.put("pageIndex", qry.getPageIndex());
        params.put("pageSize", qry.getPageSize());
        params.put("fireBrigadeName", qry.getTeam());
        params.put("alarmStatus", parseStatus(qry.getStatus()));
        params.put("feedbackOnly", Boolean.TRUE.equals(qry.getMyFeedbackOnly()));
        if (Boolean.TRUE.equals(qry.getMyFeedbackOnly())) {
            List<String> currentBrigadeIds = getCurrentUserBrigadeBizIds();
            if (currentBrigadeIds.isEmpty()) {
                return PageResponse.of(Collections.emptyList(), 0, qry.getPageSize(), qry.getPageIndex());
            }
            params.put("fireBrigadeIds", currentBrigadeIds);
        }
        PageResponse<FireAlarmInfoDO> page = fireAlarmInfoRepository.listPage(params);
        return PageResponse.of(page.getData().stream().map(this::buildCO).collect(Collectors.toList()),
                page.getTotalCount(), page.getPageSize(), page.getPageIndex());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<AlarmRecordCO> add(AlarmRecordAddCmd cmd) {
        if (!isValidLongitude(cmd.getLng()) || !isValidLatitude(cmd.getLat())) {
            throw new BizException("经纬度超出范围");
        }
        FireAlarmInfoDO record = new FireAlarmInfoDO();
        record.setId(nextId());
        record.setFireAlarmInfoId(nextBizId());
        fillCreateAudit(record);
        record.setAlarmMode(resolveAlarmMode(cmd.getAlarmMode()));
        record.setAlarmUserId(cmd.getAlarmUserId());
        record.setAlarmUserName(cmd.getReporter());
        record.setAlarmPhone(cmd.getAlarmPhone());
        record.setCorpinfoId(cmd.getCorpinfoId());
        record.setCorpinfoName(cmd.getUnit());
        record.setFireLocation(cmd.getLocation());
        record.setLocationAddress(StrUtil.isBlank(cmd.getLocationAddress()) ? cmd.getLocation() : cmd.getLocationAddress());
        record.setLongitude(BigDecimal.valueOf(cmd.getLng()));
        record.setLatitude(BigDecimal.valueOf(cmd.getLat()));
        record.setAlarmStatus(ALARM_STATUS_ALARMING);
        record.setRecordTime(LocalDateTime.now());
        fireAlarmInfoRepository.save(record);
        return SingleResponse.of(buildCO(record));
    }

    @Override
    public AlarmRecordCO get(Long id) {
        FireAlarmInfoDO record = fireAlarmInfoRepository.getById(id);
        if (record == null) {
            throw new BizException("消防报警记录不存在");
        }
        return buildCO(record);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handle(AlarmRecordHandleCmd cmd) {
        FireAlarmInfoDO record = fireAlarmInfoRepository.getById(cmd.getId());
        if (record == null) {
            throw new BizException("消防报警记录不存在");
        }
        if (!Integer.valueOf(ALARM_STATUS_ALARMING).equals(record.getAlarmStatus())) {
            throw new BizException("仅报警中的记录可处置");
        }
        FireBrigadeDO brigade = resolveHandleBrigade(cmd);
        record.setFireBrigadeId(brigade.getFireBrigadeId());
        record.setFireBrigadeName(selectText(brigade.getBrigadeName(), cmd.getFireBrigadeName()));
        record.setHandleUserId(currentUserId());
        record.setHandleUserName(currentUserName());
        record.setHandleTime(LocalDateTime.now());
        record.setAlarmStatus(ALARM_STATUS_DISPOSING);
        fillUpdateAudit(record);
        fireAlarmInfoRepository.updateById(record);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clear(AlarmRecordClearCmd cmd) {
        FireAlarmInfoDO record = fireAlarmInfoRepository.getById(cmd.getId());
        if (record == null) {
            throw new BizException("消防报警记录不存在");
        }
        if (!Integer.valueOf(ALARM_STATUS_DISPOSING).equals(record.getAlarmStatus())) {
            throw new BizException("仅处置中的记录可消警");
        }
        if (!canClearByCurrentUser(record)) {
            throw new BizException("仅当前处置消防队成员可消警");
        }
        record.setClearReason(cmd.getDesc().trim());
        record.setClearPhotos(toPhotoJson(cmd.getPhotos()));
        record.setClearId(currentUserId());
        record.setClearName(currentUserName());
        record.setClearTime(LocalDateTime.now());
        record.setAlarmStatus(ALARM_STATUS_CLEARED);
        fillUpdateAudit(record);
        fireAlarmInfoRepository.updateById(record);
    }

    private FireBrigadeDO resolveHandleBrigade(AlarmRecordHandleCmd cmd) {
        if (cmd.getFireBrigadeId() != null) {
            FireBrigadeDO brigade = fireBrigadeRepository.getById(cmd.getFireBrigadeId());
            if (brigade == null) {
                throw new BizException("处置消防队伍不存在");
            }
            if (!isCurrentUserInBrigade(brigade.getFireBrigadeId())) {
                throw new BizException("只能处置当前登录人所在的消防队报警");
            }
            return brigade;
        }
        List<FireBrigadeUserDO> relations = currentUserRelations();
        if (relations.isEmpty()) {
            throw new BizException("当前登录人不是消防队成员，不能执行处置");
        }
        if (relations.size() > 1) {
            throw new BizException("当前登录人存在多个消防队归属，请明确选择处置队伍");
        }
        FireBrigadeDO brigade = fireBrigadeRepository.getByFireBrigadeId(relations.get(0).getFireBrigadeId());
        if (brigade == null) {
            throw new BizException("当前登录人所属消防队不存在");
        }
        return brigade;
    }

    private AlarmRecordCO buildCO(FireAlarmInfoDO record) {
        AlarmRecordCO co = alarmRecordCoConvertor.converDOToCO(record);
        co.setAlarmUserId(record.getAlarmUserId());
        co.setSourceCode(record.getAlarmMode() == null ? null : String.valueOf(record.getAlarmMode()));
        co.setSource(sourceName(record.getAlarmMode()));
        co.setFireBrigadeId(resolveFireBrigadePkId(record.getFireBrigadeId()));
        co.setRecordTime(formatTime(record.getRecordTime()));
        co.setStatusCode(record.getAlarmStatus() == null ? null : String.valueOf(record.getAlarmStatus()));
        co.setStatus(statusName(record.getAlarmStatus()));
        co.setHandleUserId(record.getHandleUserId());
        co.setHandleUserName(record.getHandleUserName());
        co.setHandleTime(formatTime(record.getHandleTime()));
        co.setClearId(record.getClearId());
        co.setClearName(record.getClearName());
        co.setClearTime(formatTime(record.getClearTime()));
        co.setPhotos(parsePhotos(record.getClearPhotos()));
        return co;
    }

    private String toPhotoJson(List<String> photos) {
        if (photos == null || photos.isEmpty()) {
            return null;
        }
        List<String> paths = photos.stream()
                .filter(StrUtil::isNotBlank)
                .map(String::trim)
                .collect(Collectors.toList());
        return paths.isEmpty() ? null : JSONUtil.toJsonStr(paths);
    }

    private List<String> parsePhotos(String clearPhotos) {
        if (StrUtil.isBlank(clearPhotos)) {
            return Collections.emptyList();
        }
        return JSONUtil.toList(clearPhotos, String.class);
    }

    private Integer resolveAlarmMode(Integer alarmMode) {
        if (alarmMode == null) {
            return ALARM_MODE_PHONE;
        }
        if (!Integer.valueOf(ALARM_MODE_PHONE).equals(alarmMode)
                && !Integer.valueOf(ALARM_MODE_APP).equals(alarmMode)) {
            throw new BizException("接报方式无效");
        }
        return alarmMode;
    }

    private Integer parseStatus(String status) {
        if (StrUtil.isBlank(status)) {
            return null;
        }
        try {
            return Integer.valueOf(status.trim());
        }
        catch (Exception ignored) {
            if ("报警中".equals(status)) {
                return ALARM_STATUS_ALARMING;
            }
            if ("处置中".equals(status)) {
                return ALARM_STATUS_DISPOSING;
            }
            if ("已消警".equals(status)) {
                return ALARM_STATUS_CLEARED;
            }
            return null;
        }
    }

    private String sourceName(Integer alarmMode) {
        if (Integer.valueOf(ALARM_MODE_APP).equals(alarmMode)) {
            return "APP报警";
        }
        return "电话接报";
    }

    private String statusName(Integer alarmStatus) {
        if (Integer.valueOf(ALARM_STATUS_ALARMING).equals(alarmStatus)) {
            return "报警中";
        }
        if (Integer.valueOf(ALARM_STATUS_DISPOSING).equals(alarmStatus)) {
            return "处置中";
        }
        if (Integer.valueOf(ALARM_STATUS_CLEARED).equals(alarmStatus)) {
            return "已消警";
        }
        return null;
    }

    private String formatTime(LocalDateTime time) {
        return time == null ? null : TIME_FORMATTER.format(time);
    }

    private Long resolveFireBrigadePkId(String fireBrigadeBizId) {
        if (StrUtil.isBlank(fireBrigadeBizId)) {
            return null;
        }
        FireBrigadeDO brigade = fireBrigadeRepository.getByFireBrigadeId(fireBrigadeBizId);
        return brigade == null ? null : brigade.getId();
    }

    private boolean canClearByCurrentUser(FireAlarmInfoDO record) {
        if (record.getFireBrigadeId() == null || currentUserId() == null) {
            return false;
        }
        return fireBrigadeUserRepository.existsRelation(record.getFireBrigadeId(), currentUserId(), null);
    }

    private boolean isCurrentUserInBrigade(String fireBrigadeBizId) {
        if (StrUtil.isBlank(fireBrigadeBizId) || currentUserId() == null) {
            return false;
        }
        return fireBrigadeUserRepository.existsRelation(fireBrigadeBizId, currentUserId(), null);
    }

    private List<FireBrigadeUserDO> currentUserRelations() {
        if (currentUserId() == null) {
            return Collections.emptyList();
        }
        return fireBrigadeUserRepository.listByUserId(currentUserId());
    }

    private List<String> getCurrentUserBrigadeBizIds() {
        return currentUserRelations().stream()
                .map(FireBrigadeUserDO::getFireBrigadeId)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
    }

    private boolean isValidLongitude(Double lng) {
        return lng != null && lng >= -180D && lng <= 180D;
    }

    private boolean isValidLatitude(Double lat) {
        return lat != null && lat >= -90D && lat <= 90D;
    }

    private String selectText(String preferred, String fallback) {
        return StrUtil.isNotBlank(preferred) ? preferred : fallback;
    }

    private long nextId() {
        return IdUtil.getSnowflake(1, 1).nextId();
    }

    private String nextBizId() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}
