package db

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"net"
	"strings"

	"github.com/go-sql-driver/mysql"
)

// IsConnLost reports whether err means the database was UNREACHABLE (network
// down, server gone, dead pooled connection) as opposed to a legitimate
// server-side SQL error (constraint violation, bad data, syntax).
//
// This distinction is the linchpin of the offline safety net: a QSO is parked in
// the local ADIF outbox ONLY on a connection loss. If we queued on any error, a
// genuine data bug would silently vanish into the file instead of being
// reported — the worst possible outcome.
//
// A *mysql.MySQLError means the SERVER answered and rejected us, so it is never
// a connection loss, whatever its code.
func IsConnLost(err error) bool {
	if err == nil {
		return false
	}
	// The server responded → a real SQL error, not a lost link.
	var me *mysql.MySQLError
	if errors.As(err, &me) {
		return false
	}
	if errors.Is(err, driver.ErrBadConn) || errors.Is(err, sql.ErrConnDone) || errors.Is(err, sql.ErrTxDone) {
		return true
	}
	var ne net.Error
	if errors.As(err, &ne) {
		return true
	}
	var oe *net.OpError
	if errors.As(err, &oe) {
		return true
	}
	// The MySQL driver reports a few of these as plain strings.
	s := strings.ToLower(err.Error())
	for _, p := range []string{
		"invalid connection",
		"bad connection",
		"connection refused",
		"connection reset",
		"broken pipe",
		"no such host",
		"i/o timeout",
		"dial tcp",
		"unexpected eof",
		"driver: bad connection",
		"can't connect",
		"network is unreachable",
		"host is unreachable",
	} {
		if strings.Contains(s, p) {
			return true
		}
	}
	return false
}
