package com.writeoff.module.notification.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.writeoff.common.exception.BusinessException;
import com.writeoff.module.notification.dto.SavePlatformNotifyGatewayRequest;
import com.writeoff.module.notification.model.PlatformNotifyGatewayInfo;
import com.writeoff.module.notification.model.PlatformNotifyGatewayResolvedConfig;
import com.writeoff.security.AuthContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

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

@Service
public class PlatformNotifyGatewayService {
    private final JdbcTemplate jdbcTemplate;
    private final NotificationGatewayCryptoService cryptoService;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public PlatformNotifyGatewayService(JdbcTemplate jdbcTemplate, NotificationGatewayCryptoService cryptoService) {
        this.jdbcTemplate = jdbcTemplate;
        this.cryptoService = cryptoService;
    }

    public List<PlatformNotifyGatewayInfo> list() {
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT id, channel_code, gateway_name, provider_code, status, config_json, secret_config_cipher, remark, " +
                        "DATE_FORMAT(updated_at, '%Y-%m-%d %H:%i:%s') AS updated_at " +
                        "FROM platform_notify_gateway WHERE is_deleted=0 ORDER BY id ASC"
        );
        List<PlatformNotifyGatewayInfo> list = new ArrayList<PlatformNotifyGatewayInfo>();
        for (Map<String, Object> row : rows) {
            list.add(toInfo(row));
        }
        return list;
    }

    public PlatformNotifyGatewayInfo save(String channelCode, SavePlatformNotifyGatewayRequest request) {
        String normalizedChannel = normalizeChannel(channelCode);
        Map<String, Object> existing = findRow(normalizedChannel);
        if (existing.isEmpty()) {
            throw new BusinessException(10003, "通知网关不存在");
        }
        SaveConfigBundle bundle = buildConfigBundle(
                normalizedChannel,
                request == null ? null : request.getConfig(),
                request == null ? null : request.getProviderCode(),
                request == null ? null : request.getStatus()
        );
        String gatewayName = normalizeText(
                request == null ? null : request.getGatewayName(),
                normalizedChannel.equals("EMAIL") ? "邮件网关" : "短信网关"
        );
        String remark = request == null ? null : normalizeNullableText(request.getRemark());
        jdbcTemplate.update(
                "UPDATE platform_notify_gateway SET gateway_name=?, provider_code=?, status=?, config_json=?, secret_config_cipher=?, remark=?, updated_by=?, updated_at=CURRENT_TIMESTAMP " +
                        "WHERE channel_code=? AND is_deleted=0",
                gatewayName,
                bundle.providerCode,
                bundle.status,
                toJson(bundle.publicConfig),
                cryptoService.encrypt(toJson(bundle.secretConfig)),
                remark,
                safeUserId(),
                normalizedChannel
        );
        return toInfo(findRow(normalizedChannel));
    }

    public PlatformNotifyGatewayResolvedConfig resolveChannelConfig(String channelCode, boolean requireEnabled) {
        String normalizedChannel = normalizeChannel(channelCode);
        Map<String, Object> row = findRow(normalizedChannel);
        if (row.isEmpty()) {
            return null;
        }
        String status = String.valueOf(row.get("status"));
        if (requireEnabled && !"ENABLED".equalsIgnoreCase(status)) {
            return null;
        }
        Map<String, Object> mergedConfig = mergeConfig(row);
        return new PlatformNotifyGatewayResolvedConfig(
                toLong(row.get("id")),
                normalizedChannel,
                String.valueOf(row.get("gateway_name")),
                String.valueOf(row.get("provider_code")),
                status,
                row.get("remark") == null ? "" : String.valueOf(row.get("remark")),
                mergedConfig
        );
    }

    private PlatformNotifyGatewayInfo toInfo(Map<String, Object> row) {
        Map<String, Object> mergedConfig = mergeConfig(row);
        return new PlatformNotifyGatewayInfo(
                toLong(row.get("id")),
                String.valueOf(row.get("channel_code")),
                String.valueOf(row.get("gateway_name")),
                String.valueOf(row.get("provider_code")),
                String.valueOf(row.get("status")),
                row.get("remark") == null ? "" : String.valueOf(row.get("remark")),
                isConfigured(String.valueOf(row.get("channel_code")), mergedConfig),
                row.get("updated_at") == null ? "" : String.valueOf(row.get("updated_at")),
                mergedConfig
        );
    }

    private SaveConfigBundle buildConfigBundle(String channelCode, Map<String, Object> input, String providerCodeRaw, String statusRaw) {
        Map<String, Object> safeInput = input == null ? new LinkedHashMap<String, Object>() : input;
        String status = normalizeStatus(statusRaw);
        if ("EMAIL".equals(channelCode)) {
            String providerCode = normalizeText(providerCodeRaw, "SMTP").toUpperCase(Locale.ROOT);
            Map<String, Object> publicConfig = new LinkedHashMap<String, Object>();
            Map<String, Object> secretConfig = new LinkedHashMap<String, Object>();
            publicConfig.put("host", normalizeNullableText(safeInput.get("host")));
            publicConfig.put("port", normalizeInt(safeInput.get("port"), 587));
            publicConfig.put("protocol", normalizeText(safeInput.get("protocol"), "smtp"));
            publicConfig.put("fromAddress", normalizeNullableText(safeInput.get("fromAddress")));
            publicConfig.put("defaultSubject", normalizeText(safeInput.get("defaultSubject"), "系统通知"));
            publicConfig.put("smtpAuth", normalizeBoolean(safeInput.get("smtpAuth"), true));
            publicConfig.put("starttlsEnable", normalizeBoolean(safeInput.get("starttlsEnable"), true));
            publicConfig.put("starttlsRequired", normalizeBoolean(safeInput.get("starttlsRequired"), false));
            publicConfig.put("sslEnable", normalizeBoolean(safeInput.get("sslEnable"), false));
            publicConfig.put("connectTimeoutMs", normalizeInt(safeInput.get("connectTimeoutMs"), 5000));
            publicConfig.put("timeoutMs", normalizeInt(safeInput.get("timeoutMs"), 5000));
            publicConfig.put("writeTimeoutMs", normalizeInt(safeInput.get("writeTimeoutMs"), 5000));
            publicConfig.put("failureThreshold", normalizeInt(safeInput.get("failureThreshold"), 3));
            publicConfig.put("breakerCooldownSeconds", normalizeInt(safeInput.get("breakerCooldownSeconds"), 300));
            secretConfig.put("username", normalizeNullableText(safeInput.get("username")));
            secretConfig.put("password", normalizeNullableText(safeInput.get("password")));
            if ("ENABLED".equals(status)) {
                assertRequired(publicConfig.get("host"), "SMTP 主机不能为空");
                assertRequired(publicConfig.get("fromAddress"), "发件邮箱不能为空");
                if (Boolean.TRUE.equals(publicConfig.get("smtpAuth"))) {
                    assertRequired(secretConfig.get("username"), "SMTP 用户名不能为空");
                    assertRequired(secretConfig.get("password"), "SMTP 密码不能为空");
                }
            }
            return new SaveConfigBundle(providerCode, status, publicConfig, secretConfig);
        }

        if ("SMS".equals(channelCode)) {
            String providerCode = normalizeText(providerCodeRaw, "MOCK").toUpperCase(Locale.ROOT);
            Map<String, Object> publicConfig = new LinkedHashMap<String, Object>();
            Map<String, Object> secretConfig = new LinkedHashMap<String, Object>();
            boolean mockEnabled = normalizeBoolean(safeInput.get("mockEnabled"), "MOCK".equals(providerCode));
            publicConfig.put("endpoint", normalizeNullableText(safeInput.get("endpoint")));
            publicConfig.put("signName", normalizeNullableText(safeInput.get("signName")));
            publicConfig.put("templateCode", normalizeNullableText(safeInput.get("templateCode")));
            publicConfig.put("regionId", normalizeText(safeInput.get("regionId"), "cn-hangzhou"));
            publicConfig.put("mockEnabled", mockEnabled);
            publicConfig.put("quietPeriodSeconds", normalizeInt(safeInput.get("quietPeriodSeconds"), 30));
            publicConfig.put("dailyLimit", normalizeInt(safeInput.get("dailyLimit"), 10));
            publicConfig.put("failureThreshold", normalizeInt(safeInput.get("failureThreshold"), 3));
            publicConfig.put("breakerCooldownSeconds", normalizeInt(safeInput.get("breakerCooldownSeconds"), 300));
            secretConfig.put("accessKeyId", normalizeNullableText(safeInput.get("accessKeyId")));
            secretConfig.put("accessKeySecret", normalizeNullableText(safeInput.get("accessKeySecret")));
            if ("ENABLED".equals(status) && !mockEnabled) {
                assertRequired(publicConfig.get("endpoint"), "短信服务地址不能为空");
                assertRequired(publicConfig.get("signName"), "短信签名不能为空");
                assertRequired(secretConfig.get("accessKeyId"), "短信 AccessKeyId 不能为空");
                assertRequired(secretConfig.get("accessKeySecret"), "短信 AccessKeySecret 不能为空");
            }
            return new SaveConfigBundle(providerCode, status, publicConfig, secretConfig);
        }

        throw new BusinessException(10001, "暂不支持的通知网关渠道");
    }

    private Map<String, Object> mergeConfig(Map<String, Object> row) {
        Map<String, Object> merged = new LinkedHashMap<String, Object>();
        merged.putAll(parseJsonObject(row.get("config_json") == null ? null : String.valueOf(row.get("config_json"))));
        String secretJson = cryptoService.decrypt(row.get("secret_config_cipher") == null ? null : String.valueOf(row.get("secret_config_cipher")));
        merged.putAll(parseJsonObject(secretJson));
        return merged;
    }

    private Map<String, Object> findRow(String channelCode) {
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT id, channel_code, gateway_name, provider_code, status, config_json, secret_config_cipher, remark, " +
                        "DATE_FORMAT(updated_at, '%Y-%m-%d %H:%i:%s') AS updated_at " +
                        "FROM platform_notify_gateway WHERE channel_code=? AND is_deleted=0 LIMIT 1",
                channelCode
        );
        return rows.isEmpty() ? new LinkedHashMap<String, Object>() : rows.get(0);
    }

    private Map<String, Object> parseJsonObject(String json) {
        if (json == null || json.trim().isEmpty()) {
            return new LinkedHashMap<String, Object>();
        }
        try {
            Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
            return map == null ? new LinkedHashMap<String, Object>() : new LinkedHashMap<String, Object>(map);
        } catch (Exception ex) {
            throw new BusinessException(10001, "通知网关配置格式非法");
        }
    }

    private String toJson(Map<String, Object> map) {
        try {
            return objectMapper.writeValueAsString(map == null ? new LinkedHashMap<String, Object>() : map);
        } catch (Exception ex) {
            throw new BusinessException(10001, "通知网关配置序列化失败");
        }
    }

    private boolean isConfigured(String channelCode, Map<String, Object> config) {
        if ("EMAIL".equals(channelCode)) {
            return !normalizeNullableText(config.get("host")).isEmpty() && !normalizeNullableText(config.get("fromAddress")).isEmpty();
        }
        if ("SMS".equals(channelCode)) {
            return normalizeBoolean(config.get("mockEnabled"), false) || !normalizeNullableText(config.get("endpoint")).isEmpty();
        }
        return !config.isEmpty();
    }

    private void assertRequired(Object value, String message) {
        if (normalizeNullableText(value).isEmpty()) {
            throw new BusinessException(10001, message);
        }
    }

    private String normalizeChannel(String channelCode) {
        String value = normalizeText(channelCode, "").toUpperCase(Locale.ROOT);
        if (!"EMAIL".equals(value) && !"SMS".equals(value)) {
            throw new BusinessException(10001, "通知网关渠道仅支持 EMAIL 或 SMS");
        }
        return value;
    }

    private String normalizeStatus(String status) {
        String value = normalizeText(status, "DISABLED").toUpperCase(Locale.ROOT);
        if (!"ENABLED".equals(value) && !"DISABLED".equals(value)) {
            throw new BusinessException(10001, "网关状态仅支持 ENABLED 或 DISABLED");
        }
        return value;
    }

    private String normalizeText(Object value, String defaultValue) {
        String text = normalizeNullableText(value);
        return text.isEmpty() ? defaultValue : text;
    }

    private String normalizeNullableText(Object value) {
        return value == null ? "" : String.valueOf(value).trim();
    }

    private Integer normalizeInt(Object value, int defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        try {
            int parsed = value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(String.valueOf(value).trim());
            return parsed <= 0 ? defaultValue : parsed;
        } catch (Exception ex) {
            return defaultValue;
        }
    }

    private Boolean normalizeBoolean(Object value, boolean defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        String text = String.valueOf(value).trim();
        if ("true".equalsIgnoreCase(text) || "1".equals(text) || "Y".equalsIgnoreCase(text)) {
            return true;
        }
        if ("false".equalsIgnoreCase(text) || "0".equals(text) || "N".equalsIgnoreCase(text)) {
            return false;
        }
        return defaultValue;
    }

    private Long toLong(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.valueOf(String.valueOf(value).trim());
        } catch (Exception ex) {
            return null;
        }
    }

    private Long safeUserId() {
        Long userId = AuthContext.userId();
        return userId == null ? 0L : userId;
    }

    private static final class SaveConfigBundle {
        private final String providerCode;
        private final String status;
        private final Map<String, Object> publicConfig;
        private final Map<String, Object> secretConfig;

        private SaveConfigBundle(String providerCode, String status, Map<String, Object> publicConfig, Map<String, Object> secretConfig) {
            this.providerCode = providerCode;
            this.status = status;
            this.publicConfig = publicConfig;
            this.secretConfig = secretConfig;
        }
    }
}
