package storage

import "strings"

// Network-mount verify failure classification — the categorized refusal behind verify-before-commit.
// The categories and their journal substrings are the LIVE-MEASURED error taxonomy of
// SPIKE-nas-verify-2026-07-11 §Q4 (felhom.eu/documentation/audits/): every mount failure exits
// rc=32, so classification MUST run on the journal's message strings, never on exit codes — an
// exit-code classifier cannot even split wrong-password from wrong-share-name.

// Verify failure category codes. The controller maps these to the customer-facing Hungarian
// messages; the codes themselves are a stable wire vocabulary — do not rename casually.
const (
	NetVerifyUnreachable = "unreachable"  // no host behind the endpoint (pre-probe or No route/refused)
	NetVerifyNFSExport   = "nfs_export"   // NFS export missing OR not permitted — MERGED (Q4 ii≡iii)
	NetVerifySMBAuth     = "smb_auth"     // SMB wrong username/password (mount error(13))
	NetVerifySMBShare    = "smb_share"    // SMB share name not found (mount error(2))
	NetVerifyTimeout     = "timeout"      // blocked until systemd's 90 s start cap (black-holed-but-routed)
	NetVerifyMountFailed = "mount_failed" // no diagnostic matched / journal unavailable
)

// netVerifyRule is one first-match-wins row of the classification table. The substrings are
// VERBATIM from the spike's Q4 transcripts (mount.nfs4 / mount.cifs / systemd on PVE 8) — matching
// is on the stable fragment, tolerant of surrounding version drift.
type netVerifyRule struct {
	substr string
	code   string
	hint   string
}

// netVerifyRules — ordered: protocol-specific diagnostics before generic ones. Note the NFS rule
// keys on the full "reason given by server:" fragment, so SMB's "mount error(2): No such file or
// directory" can never shadow it (and vice versa).
var netVerifyRules = []netVerifyRule{
	{"No route to host", NetVerifyUnreachable, "no route to the server (retry=0 fast-fail)"},
	{"Connection refused", NetVerifyUnreachable, "the server refused the connection"},
	{"Connection timed out", NetVerifyUnreachable, "the connection timed out"},
	{"reason given by server: No such file or directory", NetVerifyNFSExport,
		"NFS export not found OR not permitted for this client — NFSv4 cannot distinguish the two (SPIKE Q4 ii≡iii)"},
	{"mount error(13)", NetVerifySMBAuth, "SMB logon failure (STATUS_LOGON_FAILURE) — wrong username or password"},
	{"mount error(2)", NetVerifySMBShare, "SMB share not found (BAD_NETWORK_NAME)"},
	{"Mounting timed out. Terminating", NetVerifyTimeout,
		"mount blocked until systemd's start timeout — server routed but not answering"},
}

// ClassifyNetVerifyFailure maps a mount unit's journal tail to a verify failure category + an
// operator-facing English hint (the Hungarian customer message is the controller's job). Pure,
// table-driven, first match wins. tcpReachable (the endpoint pre-probe result at classification
// time) only breaks the tie when NO substring matched: an empty/unmatched journal against a
// dead endpoint is still `unreachable`, not the generic `mount_failed`.
func ClassifyNetVerifyFailure(journalTail string, tcpReachable bool) (code, hint string) {
	for _, r := range netVerifyRules {
		if strings.Contains(journalTail, r.substr) {
			return r.code, r.hint
		}
	}
	if !tcpReachable {
		return NetVerifyUnreachable, "no mount diagnostic in the journal and the endpoint is not reachable"
	}
	return NetVerifyMountFailed, "mount failed with no recognized diagnostic — see the raw journal detail"
}
