package partners

import (
	"context"
	"errors"
	"strings"
	"testing"
)

// The customer-password-reset recovery path (000151): an ERP operator sets a
// fresh B2B login password for a locked-out buyer. Store fixture mirrors the
// real statements: CustomerLoginUser resolves the linked login (NULL when the
// customer has none), SetUserPassword overwrites the hash.
func resetFixture(customerID int64, hasLogin bool, loginActive bool) (*Service, *fakeStore, *[]string) {
	fs := newFakeStore()
	fs.customers[customerID] = customerRow(customerID)
	if hasLogin {
		fs.customerUsers[customerID] = customerLogin{ID: 900, IsActive: loginActive}
	}
	revoked := &[]string{}
	s := NewService(fs, nil, testLoc()).WithOnCustomerPasswordReset(func(_ context.Context, userID int64) error {
		*revoked = append(*revoked, "user")
		return nil
	})
	_ = revoked
	return s, fs, revoked
}

func TestResetCustomerPassword_HappyPathHashesAndRevokes(t *testing.T) {
	s, fs, revoked := resetFixture(10, true, true)

	if err := s.ResetCustomerPassword(context.Background(), 10, "Sandbox!Baru99"); err != nil {
		t.Fatalf("reset err = %v, want nil", err)
	}
	hash, ok := fs.passwords[900]
	if !ok || len(hash) == 0 {
		t.Fatal("password hash not written")
	}
	if strings.HasPrefix(hash, "Sandbox") || hash == "Sandbox!Baru99" {
		t.Error("password stored in plaintext — must be a bcrypt hash")
	}
	if len(*revoked) != 1 {
		t.Errorf("token revocation ran %d times, want 1 (a stolen session must not outlive the reset)", len(*revoked))
	}
}

func TestResetCustomerPassword_NoLoginRefused(t *testing.T) {
	s, _, _ := resetFixture(10, false, true)
	if err := s.ResetCustomerPassword(context.Background(), 10, "Sandbox!Baru99"); err == nil {
		t.Fatal("a customer without a linked login must be refused — nothing to reset")
	}
}

func TestResetCustomerPassword_InactiveLoginRefused(t *testing.T) {
	s, _, _ := resetFixture(10, true, false)
	if err := s.ResetCustomerPassword(context.Background(), 10, "Sandbox!Baru99"); err == nil {
		t.Fatal("an inactive login must be refused — reset implies an active account")
	}
}

func TestResetCustomerPassword_StrengthRulesMirrorRegistration(t *testing.T) {
	s, _, _ := resetFixture(10, true, true)
	for _, pw := range []string{"pendek", "password123", "12345678"} {
		if err := s.ResetCustomerPassword(context.Background(), 10, pw); err == nil {
			t.Errorf("weak password %q accepted — must follow the registration strength rules", pw)
		}
	}
}

func TestResetCustomerPassword_MissingCustomerNotFound(t *testing.T) {
	s, _, _ := resetFixture(10, true, true)
	if err := s.ResetCustomerPassword(context.Background(), 999, "Sandbox!Baru99"); !errors.Is(err, ErrNotFound) {
		t.Fatalf("err = %v, want ErrNotFound", err)
	}
}
