package bootstrap

import (
	"context"
	"testing"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/model/member/domain/entity"
	domrepo "haixun-backend/internal/model/member/domain/repository"

	"golang.org/x/crypto/bcrypt"
)

type memoryMemberRepo struct {
	byEmail map[string]*entity.Member
}

func memberKey(tenantID, email string) string {
	return tenantID + ":" + normalizeEmail(email)
}

func (m *memoryMemberRepo) EnsureIndexes(context.Context) error { return nil }

func (m *memoryMemberRepo) Create(_ context.Context, member *entity.Member) (*entity.Member, error) {
	if m.byEmail == nil {
		m.byEmail = map[string]*entity.Member{}
	}
	key := memberKey(member.TenantID, member.Email)
	if _, ok := m.byEmail[key]; ok {
		return nil, app.For(code.Member).ResConflict("member already exists")
	}
	cp := *member
	m.byEmail[key] = &cp
	return &cp, nil
}

func (m *memoryMemberRepo) FindByUID(_ context.Context, tenantID, uid string) (*entity.Member, error) {
	for _, item := range m.byEmail {
		if item.TenantID == tenantID && item.UID == uid {
			cp := *item
			return &cp, nil
		}
	}
	return nil, app.For(code.Member).ResNotFound("member not found")
}

func (m *memoryMemberRepo) FindByEmail(_ context.Context, tenantID, email string) (*entity.Member, error) {
	item, ok := m.byEmail[memberKey(tenantID, email)]
	if !ok {
		return nil, app.For(code.Member).ResNotFound("member not found")
	}
	cp := *item
	return &cp, nil
}

func (m *memoryMemberRepo) UpdateProfile(context.Context, string, string, domrepo.ProfileUpdate) (*entity.Member, error) {
	return nil, app.For(code.Member).SysNotImplemented("not implemented")
}

func (m *memoryMemberRepo) SetActiveThreadsAccountID(_ context.Context, tenantID, uid, accountID string) error {
	for _, item := range m.byEmail {
		if item.TenantID == tenantID && item.UID == uid {
			item.ActiveThreadsAccountID = accountID
			return nil
		}
	}
	return app.For(code.Member).ResNotFound("member not found")
}

func (m *memoryMemberRepo) SetRoles(_ context.Context, tenantID, uid string, roles []string) error {
	for _, item := range m.byEmail {
		if item.TenantID == tenantID && item.UID == uid {
			item.Roles = append([]string(nil), roles...)
			return nil
		}
	}
	return app.For(code.Member).ResNotFound("member not found")
}

func TestEnsureAdminMemberCreatesAdmin(t *testing.T) {
	repo := &memoryMemberRepo{byEmail: map[string]*entity.Member{}}
	member, created, err := EnsureAdminMember(context.Background(), repo, AdminOptions{
		TenantID: "default",
		Email:    "admin@haixun.local",
		Password: "Admin-Pass-1!",
	})
	if err != nil {
		t.Fatalf("EnsureAdminMember: %v", err)
	}
	if !created {
		t.Fatal("expected created=true")
	}
	if member.Roles[0] != "admin" {
		t.Fatalf("roles=%v", member.Roles)
	}
	if err := bcrypt.CompareHashAndPassword([]byte(member.PasswordHash), []byte("Admin-Pass-1!")); err != nil {
		t.Fatalf("password hash mismatch: %v", err)
	}
}

func TestEnsureAdminMemberUpgradesExisting(t *testing.T) {
	repo := &memoryMemberRepo{byEmail: map[string]*entity.Member{
		memberKey("default", "admin@haixun.local"): {
			TenantID: "default",
			UID:      "uid-1",
			Email:    "admin@haixun.local",
			Roles:    []string{"user"},
		},
	}}
	_, created, err := EnsureAdminMember(context.Background(), repo, AdminOptions{
		TenantID: "default",
		Email:    "admin@haixun.local",
		Password: "Admin-Pass-1!",
	})
	if err != nil {
		t.Fatalf("EnsureAdminMember: %v", err)
	}
	if created {
		t.Fatal("expected created=false")
	}
	member := repo.byEmail[memberKey("default", "admin@haixun.local")]
	if len(member.Roles) != 1 || member.Roles[0] != "admin" {
		t.Fatalf("roles=%v", member.Roles)
	}
}
