package settings

import (
	"log"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func newSMBTestSettings(t *testing.T) (*Settings, string) {
	t.Helper()
	dir := t.TempDir()
	path := filepath.Join(dir, "settings.json")
	s, err := Load(path, log.New(os.Stderr, "", 0))
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	return s, path
}

func TestValidateSMBServerName(t *testing.T) {
	good := []string{"FELHOM", "Otthon-1", "box_2", "A", "sixteencharnam"} // last = 14 chars
	for _, n := range good {
		if err := ValidateSMBServerName(n); err != nil {
			t.Errorf("%q should be valid: %v", n, err)
		}
	}
	bad := []string{"", "   ", "sixteencharname1", "has space", "bad/slash", "-lead", "trail-", "dot.name", "back\\slash"}
	for _, n := range bad {
		if err := ValidateSMBServerName(n); err == nil {
			t.Errorf("%q should be invalid", n)
		}
	}
}

func TestValidateSMBShareName(t *testing.T) {
	good := []string{"dokumentumok", "filmek", "csalad_kepek", "A1"}
	for _, n := range good {
		if err := ValidateSMBShareName(n); err != nil {
			t.Errorf("%q should be valid: %v", n, err)
		}
	}
	// Scenario C: "../x", >15-char, and invalid NetBIOS charset must all refuse.
	bad := []string{"", "../x", "a/b", "a\\b", "file.txt", "way-too-long-nam", "has space", "-x", "x-"}
	for _, n := range bad {
		if err := ValidateSMBShareName(n); err == nil {
			t.Errorf("%q should be invalid", n)
		}
	}
}

func TestSMBShareCRUD(t *testing.T) {
	s, path := newSMBTestSettings(t)

	sh := SMBShare{Name: "dokumentumok", Path: "/mnt/felhom-drives/scratch1/shares/dokumentumok", Offsite: true}
	if err := s.AddSMBShare(sh); err != nil {
		t.Fatalf("AddSMBShare: %v", err)
	}
	got := s.GetSMBShares()
	if len(got) != 1 || got[0].Name != "dokumentumok" || !got[0].Offsite {
		t.Fatalf("share not stored as expected: %+v", got)
	}
	if got[0].CreatedAt == "" {
		t.Error("CreatedAt not stamped on add")
	}

	// Case-insensitive collision is refused AND does not mutate the registry (Scenario C non-effect).
	if err := s.AddSMBShare(SMBShare{Name: "DOKUMENTUMOK", Path: "/other"}); err == nil {
		t.Error("case-insensitive name collision should be refused")
	}
	if len(s.GetSMBShares()) != 1 {
		t.Error("refused collision must not mutate the registry")
	}

	// Offsite (Felhőmentés) toggle persists.
	if err := s.SetSMBShareOffsite("dokumentumok", false); err != nil {
		t.Fatalf("SetSMBShareOffsite: %v", err)
	}
	if s.GetSMBShares()[0].Offsite {
		t.Error("offsite toggle did not persist")
	}

	// Persistence across reload.
	s2, err := Load(path, log.New(os.Stderr, "", 0))
	if err != nil {
		t.Fatalf("reload: %v", err)
	}
	if r := s2.GetSMBShares(); len(r) != 1 || r[0].Name != "dokumentumok" {
		t.Errorf("share did not persist across reload: %+v", r)
	}

	// Remove is config-only + errors on a missing name.
	if err := s.RemoveSMBShare("dokumentumok"); err != nil {
		t.Fatalf("RemoveSMBShare: %v", err)
	}
	if len(s.GetSMBShares()) != 0 {
		t.Error("share not removed")
	}
	if err := s.RemoveSMBShare("nope"); err == nil {
		t.Error("removing a missing share should error")
	}
}

func TestSMBSettingsDefaultsAndNoPassword(t *testing.T) {
	s, path := newSMBTestSettings(t)

	// Unconfigured → disabled with the default server name (never nil).
	def := s.GetSMBSettings()
	if def.Enabled || def.ServerName != DefaultSMBServerName {
		t.Errorf("default SMB settings wrong: %+v", def)
	}

	if err := s.SetSMBEnabled(true); err != nil {
		t.Fatal(err)
	}
	if err := s.SetSMBServerName("OTTHON"); err != nil {
		t.Fatal(err)
	}
	if err := s.SetSMBUserSet(true); err != nil {
		t.Fatal(err)
	}
	cur := s.GetSMBSettings()
	if !cur.Enabled || cur.ServerName != "OTTHON" || !cur.UserSet {
		t.Errorf("SMB settings not stored: %+v", cur)
	}

	// The household SMB password must NEVER be persisted (rule 4). The struct has no field for it;
	// prove the on-disk file carries only the UserSet flag, no plaintext secret.
	raw, err := os.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	if strings.Contains(string(raw), "password") && strings.Contains(string(raw), "smb") {
		// there is a claim_code_hash etc.; assert no *smb* password key specifically
		if strings.Contains(string(raw), "smb_password") || strings.Contains(string(raw), "\"password\": \"") {
			t.Errorf("settings.json appears to store an SMB password:\n%s", raw)
		}
	}
}
