package com.zcloud.personnel.positioning.command.query;

import com.alibaba.cola.dto.PageResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jjb.saas.framework.auth.model.SSOUser;
import com.jjb.saas.framework.auth.utils.AuthContext;
import com.zcloud.personnel.positioning.command.AlarmRuleConfigPersistence;
import com.zcloud.personnel.positioning.command.convertor.AlarmRuleCoConvertor;
import com.zcloud.personnel.positioning.command.convertor.AlarmRuleFenceRelCoConvertor;
import com.zcloud.personnel.positioning.command.convertor.AlarmRuleItemCoConvertor;
import com.zcloud.personnel.positioning.dto.AlarmRuleCompanyPageQry;
import com.zcloud.personnel.positioning.dto.AlarmRulePageQry;
import com.zcloud.personnel.positioning.dto.clientobject.AlarmRuleCO;
import com.zcloud.personnel.positioning.dto.clientobject.AlarmRuleCompanyStatCO;
import com.zcloud.personnel.positioning.dto.clientobject.AlarmRuleFenceRelCO;
import com.zcloud.personnel.positioning.dto.clientobject.AlarmRuleItemCO;
import com.zcloud.personnel.positioning.dto.clientobject.OptionCO;
import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRuleDO;
import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRuleFenceRelDO;
import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRuleItemDO;
import com.zcloud.personnel.positioning.persistence.repository.AlarmRuleFenceRelRepository;
import com.zcloud.personnel.positioning.persistence.repository.AlarmRuleItemRepository;
import com.zcloud.personnel.positioning.persistence.repository.AlarmRuleRepository;
import lombok.AllArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * web-app
 *
 * @Author fangjiakai
 * @Date 2026-07-07
 */
@Component
@AllArgsConstructor
public class AlarmRuleQueryExe {
    private final AlarmRuleRepository alarmRuleRepository;
    private final AlarmRuleFenceRelRepository alarmRuleFenceRelRepository;
    private final AlarmRuleItemRepository alarmRuleItemRepository;
    private final AlarmRuleCoConvertor alarmRuleCoConvertor;
    private final AlarmRuleFenceRelCoConvertor alarmRuleFenceRelCoConvertor;
    private final AlarmRuleItemCoConvertor alarmRuleItemCoConvertor;
    private final JdbcTemplate jdbcTemplate;

    public PageResponse<AlarmRuleCompanyStatCO> listCompanyStats(AlarmRuleCompanyPageQry qry) {
        AlarmRuleCompanyPageQry query = qry == null ? new AlarmRuleCompanyPageQry() : qry;
        int pageIndex = normalizePageIndex(query.getPageIndex());
        int pageSize = normalizePageSize(query.getPageSize());
        int offset = (pageIndex - 1) * pageSize;

        List<Object> args = new ArrayList<>();
        String fromWhere = buildCompanyStatsFromWhere(query, args);
        String countSql = "SELECT COUNT(1) FROM (SELECT corpinfo_id, corpinfo_name " + fromWhere
                + " GROUP BY corpinfo_id, corpinfo_name) t";
        Long total = jdbcTemplate.queryForObject(countSql, args.toArray(), Long.class);

        String dataSql = "SELECT corpinfo_id, corpinfo_name, COUNT(1) AS config_count, "
                + "SUM(CASE WHEN enable = 1 OR rule_status = 'ENABLED' THEN 1 ELSE 0 END) AS enabled_count, "
                + "COALESCE(MAX(last_sync_time), MAX(update_time)) AS last_sync_time "
                + fromWhere
                + " GROUP BY corpinfo_id, corpinfo_name"
                + " ORDER BY COALESCE(MAX(last_sync_time), MAX(update_time)) DESC"
                + " LIMIT ? OFFSET ?";
        List<Object> dataArgs = new ArrayList<>(args);
        dataArgs.add(pageSize);
        dataArgs.add(offset);
        List<AlarmRuleCompanyStatCO> rows = jdbcTemplate.query(dataSql, dataArgs.toArray(), this::mapCompanyStat);
        return PageResponse.of(rows, total == null ? 0 : total.intValue(), pageSize, pageIndex);
    }

    public List<OptionCO> listCompanyOptions(String keyword) {
        List<Object> args = new ArrayList<>();
        StringBuilder sql = new StringBuilder("SELECT corpinfo_id, corp_name FROM corp_info "
                + "WHERE (delete_enum IS NULL OR delete_enum = 'FALSE') "
                + "AND corpinfo_id IS NOT NULL AND corp_name IS NOT NULL");
        SSOUser ssoUser = AuthContext.getCurrentUser();
        if (ssoUser != null && ssoUser.getCompanyId() != null) {
            sql.append(" AND corpinfo_id = ?");
            args.add(String.valueOf(ssoUser.getCompanyId()));
        }
        if (StringUtils.hasText(keyword)) {
            sql.append(" AND (corp_name LIKE ? OR code LIKE ? OR contacts LIKE ? OR lr_name LIKE ?)");
            String value = "%" + keyword.trim() + "%";
            args.add(value);
            args.add(value);
            args.add(value);
            args.add(value);
        }
        sql.append(" ORDER BY corp_name ASC LIMIT 200");
        return jdbcTemplate.query(sql.toString(), args.toArray(), this::mapCompanyOption);
    }

    public PageResponse<AlarmRuleCO> execute(AlarmRulePageQry qry) {
        AlarmRulePageQry query = qry == null ? new AlarmRulePageQry() : qry;
        Map<String, Object> params = query.toHashMap();
        params.put("eqDeleteEnum", "FALSE");
        normalizeListParams(params);
        fillCorpinfo(params);
        prepareCorpinfoFallback(params);
        PageResponse<AlarmRuleDO> pageResponse = alarmRuleRepository.listPage(params);
        List<AlarmRuleCO> data = alarmRuleCoConvertor.converDOsToCOs(pageResponse.getData());
        fillFenceRelations(data);
        return PageResponse.of(data, pageResponse.getTotalCount(), pageResponse.getPageSize(), pageResponse.getPageIndex());
    }

    public List<AlarmRuleCO> listAll() {
        QueryWrapper<AlarmRuleDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("delete_enum", "FALSE");
        applyCorpinfo(queryWrapper);
        queryWrapper.orderByDesc("create_time");
        List<AlarmRuleCO> rows = alarmRuleCoConvertor.converDOsToCOs(alarmRuleRepository.list(queryWrapper));
        fillFenceRelations(rows);
        return rows;
    }

    public AlarmRuleCO queryById(Long id) {
        QueryWrapper<AlarmRuleDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        queryWrapper.eq("delete_enum", "FALSE");
        AlarmRuleDO dataObject = alarmRuleRepository.getOne(queryWrapper, false);
        AlarmRuleCO co = alarmRuleCoConvertor.converDOToCO(dataObject);
        if (co == null) {
            return null;
        }
        fillFenceRelations(Collections.singletonList(co));
        fillRuleItems(co);
        return co;
    }

    private String buildCompanyStatsFromWhere(AlarmRuleCompanyPageQry qry, List<Object> args) {
        StringBuilder sql = new StringBuilder(" FROM alarm_rule WHERE (delete_enum IS NULL OR delete_enum = 'FALSE')");
        if (qry.getEqCorpinfoId() != null) {
            sql.append(" AND corpinfo_id = ?");
            args.add(qry.getEqCorpinfoId());
        }
        if (StringUtils.hasText(qry.getLikeCorpinfoName())) {
            sql.append(" AND corpinfo_name LIKE ?");
            args.add("%" + qry.getLikeCorpinfoName().trim() + "%");
        }
        if (StringUtils.hasText(qry.getLikeContactName())) {
            sql.append(" AND EXISTS (SELECT 1 FROM corp_info ci"
                    + " WHERE CAST(ci.corpinfo_id AS CHAR) = CAST(alarm_rule.corpinfo_id AS CHAR)"
                    + " AND (ci.delete_enum IS NULL OR ci.delete_enum = 'FALSE')"
                    + " AND ci.contacts LIKE ?)");
            args.add("%" + qry.getLikeContactName().trim() + "%");
        }
        String status = normalizeStatus(qry.getEqRuleStatus());
        if (StringUtils.hasText(status)) {
            sql.append(" AND rule_status = ?");
            args.add(status);
        }
        if (qry.getEqEnable() != null) {
            sql.append(" AND enable = ?");
            args.add(qry.getEqEnable());
        }
        return sql.toString();
    }

    private AlarmRuleCompanyStatCO mapCompanyStat(ResultSet rs, int rowNum) throws SQLException {
        AlarmRuleCompanyStatCO co = new AlarmRuleCompanyStatCO();
        String corpinfoId = rs.getString("corpinfo_id");
        co.setCorpinfoId(rs.wasNull() ? null : corpinfoId);
        co.setCorpinfoName(rs.getString("corpinfo_name"));
        co.setConfigCount(rs.getInt("config_count"));
        co.setEnabledCount(rs.getInt("enabled_count"));
        Timestamp lastSyncTime = rs.getTimestamp("last_sync_time");
        co.setLastSyncTime(lastSyncTime == null ? null : lastSyncTime.toLocalDateTime());
        return co;
    }

    private OptionCO mapCompanyOption(ResultSet rs, int rowNum) throws SQLException {
        String corpinfoId = rs.getString("corpinfo_id");
        return new OptionCO(rs.getString("corp_name"), corpinfoId);
    }

    private void fillFenceRelations(List<AlarmRuleCO> rows) {
        if (rows == null || rows.isEmpty()) {
            return;
        }
        List<Long> ruleIds = rows.stream()
                .map(AlarmRuleCO::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (ruleIds.isEmpty()) {
            return;
        }
        QueryWrapper<AlarmRuleFenceRelDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("rule_id", ruleIds);
        queryWrapper.eq("delete_enum", "FALSE");
        queryWrapper.orderByAsc("id");
        List<AlarmRuleFenceRelCO> relRows = alarmRuleFenceRelCoConvertor
                .converDOsToCOs(alarmRuleFenceRelRepository.list(queryWrapper));
        Map<Long, List<AlarmRuleFenceRelCO>> relMap = relRows.stream()
                .collect(Collectors.groupingBy(AlarmRuleFenceRelCO::getRuleId));
        for (AlarmRuleCO row : rows) {
            List<AlarmRuleFenceRelCO> relList = relMap.getOrDefault(row.getId(), Collections.emptyList());
            row.setFenceRelList(relList);
            if (AlarmRuleConfigPersistence.ATTR_COMPANY_AREA.equals(row.getAlarmAttr())) {
                row.setFenceNames("无需选择");
            } else {
                row.setFenceNames(relList.stream()
                        .map(AlarmRuleFenceRelCO::getFenceName)
                        .filter(StringUtils::hasText)
                        .distinct()
                        .collect(Collectors.joining(",")));
            }
        }
    }

    private void fillRuleItems(AlarmRuleCO row) {
        QueryWrapper<AlarmRuleItemDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("rule_id", row.getId());
        queryWrapper.eq("delete_enum", "FALSE");
        queryWrapper.orderByAsc("level", "id");
        List<AlarmRuleItemCO> itemRows = alarmRuleItemCoConvertor
                .converDOsToCOs(alarmRuleItemRepository.list(queryWrapper));
        row.setRuleItemList(itemRows);
    }

    private void fillCorpinfo(Map<String, Object> params) {
        if (params.get("eqCorpinfoId") != null
                || StringUtils.hasText((String) params.get("eqCorpinfoName"))
                || StringUtils.hasText((String) params.get("likeCorpinfoName"))) {
            return;
        }
        SSOUser ssoUser = AuthContext.getCurrentUser();
        if (ssoUser != null && ssoUser.getCompanyId() != null) {
            params.put("eqCorpinfoId", String.valueOf(ssoUser.getCompanyId()));
        }
    }

    private void prepareCorpinfoFallback(Map<String, Object> params) {
        String corpinfoId = (String) params.get("eqCorpinfoId");
        String corpinfoName = (String) params.get("eqCorpinfoName");
        if (corpinfoId == null || !StringUtils.hasText(corpinfoName)) {
            return;
        }
        params.remove("eqCorpinfoId");
        params.remove("eqCorpinfoName");
        params.put("corpinfoMatchId", corpinfoId);
        params.put("corpinfoMatchName", corpinfoName.trim());
    }

    private void applyCorpinfo(QueryWrapper<AlarmRuleDO> queryWrapper) {
        SSOUser ssoUser = AuthContext.getCurrentUser();
        if (ssoUser != null && ssoUser.getCompanyId() != null) {
            queryWrapper.eq("corpinfo_id", String.valueOf(ssoUser.getCompanyId()));
        }
    }

    private void normalizeListParams(Map<String, Object> params) {
        String alarmAttr = normalizeAlarmAttr((String) params.get("eqAlarmAttr"));
        if (StringUtils.hasText(alarmAttr)) {
            params.put("eqAlarmAttr", alarmAttr);
        }
        String alarmCode = normalizeAlarmCode((String) params.get("eqAlarmCode"));
        if (StringUtils.hasText(alarmCode)) {
            params.put("eqAlarmCode", alarmCode);
        }
        String ruleStatus = normalizeStatus((String) params.get("eqRuleStatus"));
        if (StringUtils.hasText(ruleStatus)) {
            params.put("eqRuleStatus", ruleStatus);
        }
    }

    private String normalizeAlarmAttr(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        String attr = value.trim();
        if ("公司区域告警".equals(attr)) {
            return AlarmRuleConfigPersistence.ATTR_COMPANY_AREA;
        }
        if ("电子围栏告警".equals(attr)) {
            return AlarmRuleConfigPersistence.ATTR_FENCE;
        }
        return attr;
    }

    private String normalizeAlarmCode(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        String code = value.trim();
        if ("进入围栏告警".equals(code) || "禁入围栏告警".equals(code)) {
            return "CROSS_IN";
        }
        if ("离开围栏告警".equals(code) || "区域越界告警".equals(code)) {
            return "CROSS_OUT";
        }
        if ("超时停留告警".equals(code)) {
            return "RETENTION";
        }
        if ("脱岗告警".equals(code)) {
            return "LACKED";
        }
        if ("停留告警".equals(code)) {
            return "STAY";
        }
        if ("碰撞告警".equals(code)) {
            return "CRASH";
        }
        return code;
    }

    private String normalizeStatus(String status) {
        if (!StringUtils.hasText(status)) {
            return null;
        }
        String value = status.trim();
        if ("启用".equals(value) || "1".equals(value)) {
            return AlarmRuleConfigPersistence.STATUS_ENABLED;
        }
        if ("停用".equals(value) || "0".equals(value)) {
            return AlarmRuleConfigPersistence.STATUS_DISABLED;
        }
        return value;
    }

    private int normalizePageIndex(Integer pageIndex) {
        return pageIndex == null || pageIndex < 1 ? 1 : pageIndex;
    }

    private int normalizePageSize(Integer pageSize) {
        return pageSize == null || pageSize < 1 ? 20 : pageSize;
    }
}
