package ru.yandex.qe.dispenser.ws.health;

import java.io.IOException;
import java.io.OutputStream;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.kotlin.KotlinModule;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import ru.yandex.qe.dispenser.domain.health.HealthCheck;
import ru.yandex.qe.dispenser.domain.health.HealthChecks;

@WebServlet( name = "TelemetryHealthServlet",
        urlPatterns = {"/telemetry/healthcheck"},
        loadOnStartup = 1)
public class TelemetryHealthServlet extends HttpServlet {

    private static final DateTimeFormatter DATE_FORMAT_PATTERN =
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

    private HealthChecks healthChecks;
    private ObjectMapper mapper;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Map<String, HealthCheck.Result> results = healthChecks.runHealthChecks();
        resp.setContentType("application/json");
        resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store");
        if (results.isEmpty()) {
            // Also ok, should not break when there are no check at all
            resp.setStatus(HttpServletResponse.SC_OK);
        } else {
            if (results.values().stream().allMatch(HealthCheck.Result::isHealthy)) {
                resp.setStatus(HttpServletResponse.SC_OK);
            } else {
                resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            }
        }
        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
        String timestamp = DATE_FORMAT_PATTERN.format(zonedDateTime);
        Map<String, HealthCheckDto> dto = new HashMap<>();
        results.forEach((k, v) -> dto.put(k, new HealthCheckDto(
                v.isHealthy(),
                // Compatibility stub, always return zero for check duration
                0L,
                // Compatibility stub, return the same current timestamp for all checks
                timestamp
        )));
        try (OutputStream output = resp.getOutputStream()) {
            mapper.writeValue(output, dto);
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        mapper = new ObjectMapper().registerModules(new KotlinModule.Builder().build(),new Jdk8Module(),
                new JavaTimeModule());
        ServletContext servletContext = config.getServletContext();
        WebApplicationContext webApplicationContext = WebApplicationContextUtils
                .getWebApplicationContext(servletContext);
        healthChecks = webApplicationContext.getBean(HealthChecks.class);
    }

    public static final class HealthCheckDto {

        private final boolean healthy;
        private final long duration;
        private final String timestamp;

        public HealthCheckDto(boolean healthy, long duration, String timestamp) {
            this.healthy = healthy;
            this.duration = duration;
            this.timestamp = timestamp;
        }

        public boolean isHealthy() {
            return healthy;
        }

        public long getDuration() {
            return duration;
        }

        public String getTimestamp() {
            return timestamp;
        }

    }

}
