package claim

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

type fakeDir struct {
	people map[string]*Person
	err    error
	calls  int
}

func (f *fakeDir) Lookup(_ context.Context, id string) (*Person, error) {
	f.calls++
	if f.err != nil {
		return nil, f.err
	}
	return f.people[id], nil
}

func newDir() *fakeDir {
	return &fakeDir{people: map[string]*Person{
		"usr_fikri": {ID: "usr_fikri", Name: "Fikri", Role: RolePatron},
		"usr_ali":   {ID: "usr_ali", Name: "", Role: RoleUye},
		"usr_off":   {ID: "usr_off", Name: "Kapalı", Role: RoleUye, Disabled: true},
		"usr_weird": {ID: "usr_weird", Name: "Garip", Role: Role("root")},
		// Paylaşılan API anahtarının varsayılan kimliğiyle AYNI adlı gerçek bir hesap:
		"local": {ID: "local", Name: "Yerel", Role: RolePatron},
	}}
}

func TestIdentity_GercekHesapYazarOlur(t *testing.T) {
	id := NewIdentity(newDir(), true, "local")
	a, err := id.Actor(context.Background(), " usr_fikri ")
	if err != nil {
		t.Fatalf("hata: %v", err)
	}
	if a.ID != "usr_fikri" || a.Name != "Fikri" || a.Role != RolePatron {
		t.Fatalf("beklenmeyen actor: %+v", a)
	}
}

func TestIdentity_AdBossaKullaniciKimligineDuser(t *testing.T) {
	a, err := NewIdentity(newDir(), true).Actor(context.Background(), "usr_ali")
	if err != nil || a.Name != "usr_ali" || a.Role != RoleUye {
		t.Fatalf("actor=%+v err=%v", a, err)
	}
}

func TestIdentity_UyelikKapaliysaYazmaKapali(t *testing.T) {
	d := newDir()
	_, err := NewIdentity(d, false).Actor(context.Background(), "usr_fikri")
	if !errors.Is(err, ErrAuthRequired) {
		t.Fatalf("beklenen ErrAuthRequired, gelen %v", err)
	}
	if d.calls != 0 {
		t.Fatal("üyelik kapalıyken dizine bakılmamalı")
	}
}

func TestIdentity_PaylasilanAnahtarKimligiReddedilir(t *testing.T) {
	// DEFAULT_USER_ID ("local") ile aynı adlı gerçek hesap olsa bile paylaşılan anahtarla gelen
	// istek kişi sayılmaz.
	_, err := NewIdentity(newDir(), true, "local").Actor(context.Background(), "local")
	if !errors.Is(err, ErrUnverified) {
		t.Fatalf("beklenen ErrUnverified, gelen %v", err)
	}
}

func TestIdentity_BilinmeyenVeKapaliHesapReddedilir(t *testing.T) {
	id := NewIdentity(newDir(), true)
	for _, uid := range []string{"hacker", "usr_off"} {
		if _, err := id.Actor(context.Background(), uid); !errors.Is(err, ErrUnverified) {
			t.Fatalf("%s: beklenen ErrUnverified, gelen %v", uid, err)
		}
	}
}

func TestIdentity_BosKimlikVeNilCozucu(t *testing.T) {
	if _, err := NewIdentity(newDir(), true).Actor(context.Background(), "  "); !errors.Is(err, ErrNoActor) {
		t.Fatalf("boş kimlik: %v", err)
	}
	var nilID *Identity
	if _, err := nilID.Actor(context.Background(), "usr_ali"); !errors.Is(err, ErrAuthRequired) {
		t.Fatalf("nil çözücü: %v", err)
	}
	if _, err := NewIdentity(nil, true).Actor(context.Background(), "usr_ali"); !errors.Is(err, ErrAuthRequired) {
		t.Fatalf("nil dizin: %v", err)
	}
}

func TestIdentity_BilinmeyenRolYetkiKazandirmaz(t *testing.T) {
	a, err := NewIdentity(newDir(), true).Actor(context.Background(), "usr_weird")
	if err != nil || a.Role != RoleUye {
		t.Fatalf("actor=%+v err=%v", a, err)
	}
}

func TestIdentity_DepoHatasiYazmayiDurdurur(t *testing.T) {
	d := newDir()
	d.err = errors.New("mongo yok")
	if _, err := NewIdentity(d, true).Actor(context.Background(), "usr_ali"); err == nil {
		t.Fatal("depo hatasında actor üretilmemeli")
	}
}

func TestIdentity_DizinKimligiUyusmazsaReddedilir(t *testing.T) {
	d := &fakeDir{people: map[string]*Person{"usr_a": {ID: "usr_b", Role: RolePatron}}}
	if _, err := NewIdentity(d, true).Actor(context.Background(), "usr_a"); !errors.Is(err, ErrUnverified) {
		t.Fatalf("kimlik uyuşmazlığı: %v", err)
	}
}
