package com.smyhub.store.mengyastorebackendjava.service;

import com.smyhub.store.mengyastorebackendjava.config.AppProperties;
import com.smyhub.store.mengyastorebackendjava.mq.RabbitMQTopology;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Builds the same JSON shape as Go {@code admin_status.go} for the admin system-status panel.
 */
@Service
@RequiredArgsConstructor
public class SystemStatusService {

    private static final Instant START_TIME = Instant.now();

    private final AppProperties appProperties;
    private final DataSource dataSource;
    private final RedisConnectionFactory redisConnectionFactory;
    private final RabbitTemplate rabbitTemplate;

    @Value("${spring.datasource.url:}")
    private String datasourceUrl;

    @Value("${spring.datasource.username:}")
    private String datasourceUsername;

    @Value("${server.port:8080}")
    private int serverPort;

    @Value("${server.address:}")
    private String serverAddress;

    @Value("${spring.rabbitmq.host:}")
    private String rabbitHost;

    @Value("${spring.rabbitmq.port:5672}")
    private int rabbitPort;

    @Value("${spring.rabbitmq.virtual-host:/}")
    private String rabbitVhost;

    @Value("${spring.rabbitmq.username:}")
    private String rabbitUser;

    @Value("${spring.data.redis.host:}")
    private String redisHost;

    @Value("${spring.data.redis.port:6379}")
    private int redisPort;

    @Value("${spring.data.redis.database:0}")
    private int redisDb;

    public Map<String, Object> buildStatus(HttpServletRequest request) {
        Map<String, Object> root = new LinkedHashMap<>();
        root.put("backend", backendInfo(request));
        root.put("mysql", mysqlInfo());
        root.put("redis", redisInfo());
        root.put("rabbitmq", rabbitmqInfo());
        return root;
    }

    private Map<String, Object> backendInfo(HttpServletRequest request) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("status", "ok");
        m.put("appEnv", normalizeAppEnv(appProperties.getAppEnv()));
        m.put("ginMode", "—");
        String host = request.getServerName();
        int port = request.getServerPort();
        String hostHeader = request.getHeader("Host");
        m.put("requestHost", StringUtils.hasText(hostHeader) ? hostHeader : host + ":" + port);

        String listen = formatListenAddr();
        m.put("listenAddr", listen);

        String publicBase = appProperties.getPublicApiBaseUrl() != null
                ? appProperties.getPublicApiBaseUrl().strip() : "";
        if (publicBase.isEmpty()) {
            String proto = request.getHeader("X-Forwarded-Proto");
            if (!StringUtils.hasText(proto)) {
                proto = request.isSecure() ? "https" : "http";
            }
            if (StringUtils.hasText(hostHeader)) {
                publicBase = proto + "://" + hostHeader;
            } else {
                publicBase = proto + "://" + host + ":" + port;
            }
        }
        m.put("publicBaseUrl", publicBase);

        m.put("uptimeSeconds", (int) (Instant.now().getEpochSecond() - START_TIME.getEpochSecond()));
        m.put("authApiConfigured", StringUtils.hasText(appProperties.getAuthApiUrl()));
        m.put("rabbitmqEnabled", appProperties.isRabbitmqEnabled());
        m.put("redisEnabled", appProperties.isRedisEnabled());
        return m;
    }

    private String normalizeAppEnv(String raw) {
        if (raw == null) return "development";
        return switch (raw.strip().toLowerCase()) {
            case "production", "prod" -> "production";
            default -> "development";
        };
    }

    private String formatListenAddr() {
        String addr = serverAddress != null ? serverAddress.strip() : "";
        if (addr.isEmpty() || "0.0.0.0".equals(addr)) {
            return ":" + serverPort;
        }
        return addr + ":" + serverPort;
    }

    private Map<String, Object> mysqlInfo() {
        Map<String, Object> m = new LinkedHashMap<>();
        parseJdbcMysqlUrl(datasourceUrl, datasourceUsername, m);

        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT VERSION()")) {
            if (rs.next()) {
                m.put("version", rs.getString(1).trim());
            }
            m.put("status", "ok");
            appendPoolStats(m);
        } catch (Exception e) {
            m.put("status", "error");
            m.put("message", e.getMessage());
        }
        return m;
    }

    private void appendPoolStats(Map<String, Object> m) {
        try {
            if (dataSource instanceof HikariDataSource hikari) {
                HikariPoolMXBean pool = hikari.getHikariPoolMXBean();
                if (pool != null) {
                    int active = pool.getActiveConnections();
                    int idle = pool.getIdleConnections();
                    m.put("openConnections", active + idle);
                    m.put("inUse", active);
                    m.put("idle", idle);
                    m.put("waitCount", pool.getThreadsAwaitingConnection());
                }
            }
        } catch (Exception ignored) {
            // optional metrics
        }
    }

    private static void parseJdbcMysqlUrl(String jdbcUrl, String username, Map<String, Object> out) {
        if (jdbcUrl == null || jdbcUrl.isBlank()) {
            out.put("configured", false);
            return;
        }
        try {
            String rest = jdbcUrl.substring("jdbc:mysql://".length());
            int q = rest.indexOf('?');
            String core = q >= 0 ? rest.substring(0, q) : rest;
            int slash = core.indexOf('/');
            String hostPort = slash >= 0 ? core.substring(0, slash) : core;
            String database = "";
            if (slash >= 0 && slash < core.length() - 1) {
                database = core.substring(slash + 1);
            }
            String host;
            String port;
            if (hostPort.contains(":")) {
                int c = hostPort.lastIndexOf(':');
                host = hostPort.substring(0, c);
                port = hostPort.substring(c + 1);
            } else {
                host = hostPort;
                port = "3306";
            }
            out.put("configured", true);
            out.put("host", host);
            out.put("port", port);
            out.put("database", database);
            out.put("user", username != null ? username : "");
        } catch (Exception e) {
            out.put("configured", false);
            out.put("dsnParseError", e.getMessage());
        }
    }

    private Map<String, Object> redisInfo() {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("enabled", appProperties.isRedisEnabled());
        m.put("env", appProperties.getRedisEnv());
        m.put("host", redisHost);
        m.put("port", redisPort);
        m.put("dbIndex", redisDb);

        if (!appProperties.isRedisEnabled()) {
            m.put("status", "disabled");
            return m;
        }
        if (!StringUtils.hasText(redisHost)) {
            m.put("status", "misconfigured");
            m.put("message", "已启用但未配置 spring.data.redis.host");
            return m;
        }

        try (RedisConnection conn = redisConnectionFactory.getConnection()) {
            String pong = new String(conn.ping());
            if (!"PONG".equalsIgnoreCase(pong)) {
                m.put("status", "degraded");
                m.put("message", "PING 返回异常: " + pong);
                return m;
            }
            java.util.Properties infoProps = conn.info();
            if (infoProps == null || infoProps.isEmpty()) {
                m.put("status", "degraded");
                m.put("message", "PING 成功但无法读取 INFO");
                return m;
            }
            Long dbSize = conn.dbSize();
            m.put("status", "ok");
            m.put("redisVersion", nullToEmpty(infoProps.getProperty("redis_version")));
            m.put("usedMemoryHuman", nullToEmpty(infoProps.getProperty("used_memory_human")));
            m.put("keysApprox", dbSize);
        } catch (Exception e) {
            m.put("status", "error");
            m.put("message", e.getMessage());
        }
        return m;
    }

    private static String nullToEmpty(String s) {
        return s != null ? s : "";
    }

    private Map<String, Object> rabbitmqInfo() {
        String env = appProperties.getRabbitmqEnv();
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("enabled", appProperties.isRabbitmqEnabled());
        m.put("env", env);
        m.put("exchange", RabbitMQTopology.exchangeName(env));
        m.put("queue", RabbitMQTopology.queueName(env));
        m.put("routingKey", RabbitMQTopology.ORDER_EMAIL_ROUTING_KEY);
        m.put("brokerHost", rabbitHost);
        m.put("brokerPort", String.valueOf(rabbitPort));
        m.put("vhost", rabbitVhost);
        m.put("brokerUser", rabbitUser);

        if (!appProperties.isRabbitmqEnabled()) {
            m.put("status", "disabled");
            return m;
        }
        if (!StringUtils.hasText(rabbitHost)) {
            m.put("status", "misconfigured");
            m.put("message", "已启用但未配置 spring.rabbitmq.host");
            return m;
        }

        try {
            int[] mqStats = rabbitTemplate.execute(channel -> {
                var ok = channel.queueDeclarePassive(RabbitMQTopology.queueName(env));
                return new int[]{ok.getMessageCount(), ok.getConsumerCount()};
            });
            if (mqStats != null && mqStats.length >= 2) {
                m.put("messagesReady", mqStats[0]);
                m.put("consumers", mqStats[1]);
            }
            m.put("status", "ok");
        } catch (Exception e) {
            String msg = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
            if (msg.contains("NOT_FOUND") || msg.contains("404")) {
                m.put("status", "degraded");
                m.put("message", "已连接但队列不存在或未声明: " + msg);
            } else {
                m.put("status", "error");
                m.put("message", msg);
            }
        }
        return m;
    }
}
