package com.smyhub.infogenie.infogeniebackendjava.service;

import com.smyhub.infogenie.infogeniebackendjava.config.AppProperties;
import com.smyhub.infogenie.infogeniebackendjava.dto.response.DiagnosticsResponse;
import com.smyhub.infogenie.infogeniebackendjava.dto.response.HealthResponse;
import com.smyhub.infogenie.infogeniebackendjava.dto.response.SiteSourceResponse;
import com.zaxxer.hikari.HikariDataSource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Health + diagnostics payloads aligned with the Go backend and {@code AdminPage.js}.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HealthService {

    private static final Pattern JDBC_MYSQL =
            Pattern.compile("^jdbc:mysql://([^/?]+)(/([^?]+))?");
    private static final DateTimeFormatter RFC3339 = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

    private final DataSource dataSource;
    private final RedisTemplate<String, Object> redisTemplate;
    private final RestTemplate restTemplate;
    private final AppProperties appProperties;
    private final SiteConfigService siteConfigService;

    @Value("${server.port:5003}")
    private String serverPort;

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

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

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

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

    @Value("${APP_ENV:development}")
    private String appEnv;

    @Value("${MAIL_HOST:}")
    private String mailHost;

    @Value("${MAIL_PORT:465}")
    private int mailPort;

    @Value("${MAIL_USERNAME:}")
    private String mailUsername;

    @Value("${MAIL_PASSWORD:}")
    private String mailPassword;

    public HealthResponse buildHealthResponse() {
        HealthResponse resp = new HealthResponse();
        resp.setTimestamp(ZonedDateTime.now(ZoneId.systemDefault()).format(RFC3339));

        boolean mysqlOk = pingMysql();
        String dbStatus = mysqlOk ? "connected" : "disconnected";
        resp.setDatabase(dbStatus);

        HealthResponse.MysqlHealth mysql = new HealthResponse.MysqlHealth();
        mysql.setOk(mysqlOk);
        mysql.setStatus(dbStatus);
        resp.setMysql(mysql);

        resp.setRedis(buildRedisHealth());

        HealthResponse.BackendApiHealth backend = new HealthResponse.BackendApiHealth();
        backend.setOk(true);
        resp.setBackendApi(backend);

        resp.setSixtyApi(probeSixtyApi());

        boolean sixtyOk = resp.getSixtyApi() != null && resp.getSixtyApi().isOk();
        resp.setStatus(mysqlOk && sixtyOk ? "running" : "degraded");
        return resp;
    }

    public DiagnosticsResponse buildDiagnostics() {
        DiagnosticsResponse resp = new DiagnosticsResponse();

        DiagnosticsResponse.AppInfo app = new DiagnosticsResponse.AppInfo();
        app.setEnv(appEnv);
        app.setListenAddr("0.0.0.0:" + serverPort);
        app.setListenPort(serverPort);
        resp.setApp(app);

        JdbcMeta jdbc = resolveJdbcMeta();
        DiagnosticsResponse.MysqlInfo mysql = new DiagnosticsResponse.MysqlInfo();
        mysql.setHost(jdbc.host());
        mysql.setPort(jdbc.port());
        mysql.setDatabase(jdbc.database());
        mysql.setUser(jdbc.user());
        mysql.setPasswordConfigured(jdbc.passwordConfigured());
        resp.setMysql(mysql);

        resp.setRedis(buildRedisDiagnostics());

        SiteSourceResponse src = siteConfigService.get60sSource();
        DiagnosticsResponse.SixtyUpstreamInfo sixty = new DiagnosticsResponse.SixtyUpstreamInfo();
        sixty.setSourceId(src.getSourceId());
        sixty.setBaseUrl(src.getBaseUrl());
        sixty.setLabel(src.getLabel());
        resp.setSixtyUpstream(sixty);

        DiagnosticsResponse.AuthCenterInfo ac = new DiagnosticsResponse.AuthCenterInfo();
        ac.setApiUrl(appProperties.getAuthCenterUrl());
        resp.setAuthCenter(ac);

        DiagnosticsResponse.MailInfo mail = new DiagnosticsResponse.MailInfo();
        mail.setHost(mailHost.isBlank() ? null : mailHost);
        mail.setPort(mailPort);
        mail.setUsername(mailUsername.isBlank() ? null : mailUsername);
        mail.setPasswordConfigured(!mailPassword.isBlank());
        resp.setMail(mail);

        return resp;
    }

    private HealthResponse.RedisHealth buildRedisHealth() {
        HealthResponse.RedisHealth r = new HealthResponse.RedisHealth();
        r.setEnabled(appProperties.isRedisEnabled());
        if (!appProperties.isRedisEnabled()) {
            return r;
        }
        try {
            redisTemplate.opsForValue().get("__ping__");
            r.setOk(true);
        } catch (Exception e) {
            r.setOk(false);
            r.setError(e.getMessage());
        }
        return r;
    }

    private DiagnosticsResponse.RedisInfo buildRedisDiagnostics() {
        DiagnosticsResponse.RedisInfo r = new DiagnosticsResponse.RedisInfo();
        r.setEnabled(appProperties.isRedisEnabled());
        if (!appProperties.isRedisEnabled()) {
            return r;
        }
        r.setAddr(redisHost + ":" + redisPort);
        r.setLogicalDb(redisDatabase);
        r.setKeyPrefix(appProperties.getRedisKeyPrefix());
        r.setSiteCacheTtlSec((int) appProperties.getRedisSiteTtl());
        r.setPasswordConfigured(!redisPassword.isBlank());
        return r;
    }

    private HealthResponse.SixtyApiHealth probeSixtyApi() {
        SiteSourceResponse src = siteConfigService.get60sSource();
        String base = siteConfigService.get60sBaseUrl();
        HealthResponse.SixtyApiHealth sixty = new HealthResponse.SixtyApiHealth();
        sixty.setSourceId(src.getSourceId());
        sixty.setBaseUrl(src.getBaseUrl());
        sixty.setLabel(src.getLabel());

        String trimmed = base == null ? "" : base.strip();
        if (trimmed.isEmpty()) {
            sixty.setOk(false);
            sixty.setHttpStatus(0);
            sixty.setLatencyMs(0);
            sixty.setError("empty_base");
            return sixty;
        }
        String probeUrl =
                (trimmed.endsWith("/") ? trimmed.substring(0, trimmed.length() - 1) : trimmed) + "/v2/ip";
        sixty.setProbeUrl(probeUrl);

        long t0 = System.currentTimeMillis();
        try {
            ResponseEntity<String> re =
                    restTemplate.exchange(probeUrl, HttpMethod.GET, null, String.class);
            sixty.setLatencyMs(System.currentTimeMillis() - t0);
            int code = re.getStatusCode().value();
            sixty.setHttpStatus(code);
            boolean ok = code >= 200 && code < 500;
            sixty.setOk(ok);
            if (!ok) {
                sixty.setError("http_" + code);
            }
        } catch (RestClientException e) {
            sixty.setLatencyMs(System.currentTimeMillis() - t0);
            sixty.setHttpStatus(0);
            sixty.setOk(false);
            sixty.setError(e.getMessage() != null ? e.getMessage() : "request_failed");
        }
        return sixty;
    }

    private boolean pingMysql() {
        try (Connection c = dataSource.getConnection()) {
            return c.isValid(5);
        } catch (Exception e) {
            log.debug("MySQL ping failed: {}", e.getMessage());
            return false;
        }
    }

    private JdbcMeta resolveJdbcMeta() {
        if (!(dataSource instanceof HikariDataSource hds)) {
            return new JdbcMeta("—", "—", "—", "—", false);
        }
        String url = hds.getJdbcUrl();
        String host = "—";
        String port = "—";
        String db = "—";
        Matcher m = JDBC_MYSQL.matcher(url == null ? "" : url);
        if (m.find()) {
            String hostPart = m.group(1);
            int colon = hostPart.lastIndexOf(':');
            if (colon > 0) {
                host = hostPart.substring(0, colon);
                port = hostPart.substring(colon + 1);
            } else {
                host = hostPart;
                port = "3306";
            }
            if (m.group(3) != null && !m.group(3).isBlank()) {
                db = m.group(3);
            }
        }
        String user = hds.getUsername() != null ? hds.getUsername() : "—";
        String pwd = hds.getPassword();
        boolean pwdConfigured = pwd != null && !pwd.isEmpty();
        return new JdbcMeta(host, port, db, user, pwdConfigured);
    }

    private record JdbcMeta(
            String host,
            String port,
            String database,
            String user,
            boolean passwordConfigured
    ) {}
}
