package auth

import (
	"strings"
	"testing"

	"errors"
)

func TestGenerateCodeShape(t *testing.T) {
	for range 100 {
		code, err := generateCode()
		if err != nil {
			t.Fatalf("generateCode: %v", err)
		}
		if len(code) != CodeLength {
			t.Fatalf("len(code) = %d, want %d (got %q)", len(code), CodeLength, code)
		}
		for _, r := range code {
			if r < '0' || r > '9' {
				t.Fatalf("non-digit rune %q in code %q", r, code)
			}
		}
	}
}

func TestGenerateCodeRandomness(t *testing.T) {
	seen := make(map[string]struct{})
	const trials = 50
	for range trials {
		code, err := generateCode()
		if err != nil {
			t.Fatalf("generateCode: %v", err)
		}
		seen[code] = struct{}{}
	}
	// 50 trials over a 10^6 space — duplicate is astronomically unlikely.
	if len(seen) < trials-1 {
		t.Fatalf("generateCode produced too many duplicates: %d/%d unique", len(seen), trials)
	}
}

func TestHashAndVerifyCodeRoundTrip(t *testing.T) {
	const code = "654321"
	hash, err := hashCode(code)
	if err != nil {
		t.Fatalf("hashCode: %v", err)
	}
	if !strings.HasPrefix(string(hash), "$2") {
		t.Fatalf("hash does not look like bcrypt: %q", string(hash))
	}
	if err := verifyCode(hash, code); err != nil {
		t.Fatalf("verifyCode on matching code: %v", err)
	}
}

func TestVerifyCodeMismatch(t *testing.T) {
	hash, err := hashCode("111111")
	if err != nil {
		t.Fatalf("hashCode: %v", err)
	}
	err = verifyCode(hash, "222222")
	if !errors.Is(err, ErrCodeMismatch) {
		t.Fatalf("verifyCode mismatch returned %v, want ErrCodeMismatch", err)
	}
}

func TestVerifyCodeMalformedHash(t *testing.T) {
	err := verifyCode([]byte("not-a-hash"), "111111")
	if err == nil {
		t.Fatalf("verifyCode with garbage hash returned nil")
	}
	if errors.Is(err, ErrCodeMismatch) {
		t.Fatalf("malformed hash classified as mismatch: %v", err)
	}
}
