package com.lingniu.ingest.protocol.gb32960.inbound;

import com.lingniu.ingest.protocol.gb32960.model.CommandType;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class Gb32960ConnectionDiagnostics {

    private final Clock clock;
    private final ConcurrentMap<String, MutableSession> activeSessions = new ConcurrentHashMap<>();

    public Gb32960ConnectionDiagnostics() {
        this(Clock.systemUTC());
    }

    Gb32960ConnectionDiagnostics(Clock clock) {
        this.clock = clock;
    }

    public void opened(String sessionId, String peer) {
        Instant now = clock.instant();
        activeSessions.put(sessionId, new MutableSession(sessionId, peer, now));
    }

    public void received(String sessionId, CommandType command, String vin) {
        activeSessions.computeIfPresent(sessionId, (ignored, session) -> {
            session.lastInboundAt = clock.instant();
            session.lastCommandCode = command.code();
            session.lastCommand = command.name();
            session.vin = vin == null ? "" : vin;
            return session;
        });
    }

    public void platformLoginAccepted(String sessionId, String platformAccount) {
        activeSessions.computeIfPresent(sessionId, (ignored, session) -> {
            session.platformAccount = platformAccount == null ? "" : platformAccount;
            return session;
        });
    }

    public void readIdle(String sessionId) {
        activeSessions.computeIfPresent(sessionId, (ignored, session) -> {
            session.readIdleCount++;
            return session;
        });
    }

    public void closed(String sessionId) {
        activeSessions.remove(sessionId);
    }

    public List<SessionSnapshot> activeSessions() {
        Instant now = clock.instant();
        return activeSessions.values().stream()
                .map(session -> session.snapshot(now))
                .sorted(Comparator.comparing(SessionSnapshot::connectedAt))
                .toList();
    }

    public record SessionSnapshot(
            String sessionId,
            String peer,
            Instant connectedAt,
            Instant lastInboundAt,
            long idleSeconds,
            int lastCommandCode,
            String lastCommand,
            String vin,
            String platformAccount,
            long readIdleCount) {
    }

    private static final class MutableSession {
        private final String sessionId;
        private final String peer;
        private final Instant connectedAt;
        private Instant lastInboundAt;
        private int lastCommandCode;
        private String lastCommand = "";
        private String vin = "";
        private String platformAccount = "";
        private long readIdleCount;

        private MutableSession(String sessionId, String peer, Instant connectedAt) {
            this.sessionId = sessionId;
            this.peer = peer;
            this.connectedAt = connectedAt;
            this.lastInboundAt = connectedAt;
        }

        private SessionSnapshot snapshot(Instant now) {
            long idleSeconds = Math.max(0, Duration.between(lastInboundAt, now).toSeconds());
            return new SessionSnapshot(
                    sessionId,
                    peer,
                    connectedAt,
                    lastInboundAt,
                    idleSeconds,
                    lastCommandCode,
                    lastCommand,
                    vin,
                    platformAccount,
                    readIdleCount);
        }
    }
}
