package shine.db.dao;

import shine.db.SqliteDbController;
import shine.db.entities.EspPairingSettingsEntry;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public final class EspPairingSettingsDAO {

    private static volatile EspPairingSettingsDAO instance;
    private final SqliteDbController db = SqliteDbController.getInstance();

    private EspPairingSettingsDAO() { }

    public static EspPairingSettingsDAO getInstance() {
        if (instance == null) {
            synchronized (EspPairingSettingsDAO.class) {
                if (instance == null) instance = new EspPairingSettingsDAO();
            }
        }
        return instance;
    }

    public void upsert(EspPairingSettingsEntry entry) throws SQLException {
        try (Connection c = db.getConnection()) {
            upsert(c, entry);
        }
    }

    public void upsert(Connection c, EspPairingSettingsEntry entry) throws SQLException {
        String sql = """
            INSERT INTO esp_pairing_settings (
                login,
                enabled,
                password_hash,
                ttl_seconds,
                failed_attempts,
                first_failed_at_ms,
                blocked_until_ms,
                updated_at_ms
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ON CONFLICT(login) DO UPDATE SET
                enabled = excluded.enabled,
                password_hash = excluded.password_hash,
                ttl_seconds = excluded.ttl_seconds,
                failed_attempts = excluded.failed_attempts,
                first_failed_at_ms = excluded.first_failed_at_ms,
                blocked_until_ms = excluded.blocked_until_ms,
                updated_at_ms = excluded.updated_at_ms
            """;
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, entry.getLogin());
            ps.setInt(2, entry.isEnabled() ? 1 : 0);
            ps.setString(3, entry.getPasswordHash());
            ps.setInt(4, entry.getTtlSeconds());
            ps.setInt(5, entry.getFailedAttempts());
            ps.setLong(6, entry.getFirstFailedAtMs());
            ps.setLong(7, entry.getBlockedUntilMs());
            ps.setLong(8, entry.getUpdatedAtMs());
            ps.executeUpdate();
        }
    }

    public EspPairingSettingsEntry getByLogin(String login) throws SQLException {
        try (Connection c = db.getConnection()) {
            return getByLogin(c, login);
        }
    }

    public EspPairingSettingsEntry getByLogin(Connection c, String login) throws SQLException {
        String sql = """
            SELECT login, enabled, password_hash, ttl_seconds, failed_attempts, first_failed_at_ms, blocked_until_ms, updated_at_ms
            FROM esp_pairing_settings
            WHERE login = ? COLLATE NOCASE
            LIMIT 1
            """;
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, login);
            try (ResultSet rs = ps.executeQuery()) {
                if (!rs.next()) return null;
                return mapRow(rs);
            }
        }
    }

    private static EspPairingSettingsEntry mapRow(ResultSet rs) throws SQLException {
        EspPairingSettingsEntry entry = new EspPairingSettingsEntry();
        entry.setLogin(rs.getString("login"));
        entry.setEnabled(rs.getInt("enabled") != 0);
        entry.setPasswordHash(rs.getString("password_hash"));
        entry.setTtlSeconds(rs.getInt("ttl_seconds"));
        entry.setFailedAttempts(rs.getInt("failed_attempts"));
        entry.setFirstFailedAtMs(rs.getLong("first_failed_at_ms"));
        entry.setBlockedUntilMs(rs.getLong("blocked_until_ms"));
        entry.setUpdatedAtMs(rs.getLong("updated_at_ms"));
        return entry;
    }
}
