package api

import (
	"context"
	"reflect"
	"testing"

	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
	"github.com/google/uuid"
	log "github.com/sirupsen/logrus"
)

func TestCreateRoles(t *testing.T) {
	type args struct {
		ctx   context.Context
		addr  string
		roles []*apb.Role
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.CreateRolesResponse
		wantErr bool
	}{
		{
			name: "default create roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				roles: []*apb.Role{
					{
						Name:        "new role 1",
						Description: "Role 1",
						Permissions: []string{"permission 1", "permission 2"},
					},
				},
			},
			want: &apb.CreateRolesResponse{
				Status: apb.Status_STATUS_OK,
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := CreateRoles(tt.args.ctx, tt.args.addr, tt.args.roles)
			if (err != nil) != tt.wantErr {
				t.Errorf("CreateRoles() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status != tt.want.Status {
				t.Errorf("Role.CreateRoles() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGetRole(t *testing.T) {
	type args struct {
		ctx  context.Context
		addr string
		name string
		id   uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.GetRoleResponse
		wantErr bool
	}{
		{
			name: "default get role",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				name: "adminTestRole",
				id:   uuid.Nil,
			},
			want: &apb.GetRoleResponse{
				Status: apb.Status_STATUS_OK,
				Role: &apb.Role{
					Name:        "adminTestRole",
					Description: "Admin",
				},
			},
			wantErr: false,
		},
		{
			name: "error get role",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				name: "not role",
				id:   uuid.Nil,
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := GetRole(tt.args.ctx, tt.args.addr, tt.args.name, tt.args.id)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetRole() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status == tt.want.Status {
				if got.Role.Name != tt.want.Role.Name || got.Role.Description != tt.want.Role.Description {
					t.Errorf("Role.GetRole() = %v, want %v", got, tt.want)
				}
			} else {
				if got != nil {
					t.Errorf("Role.GetRole() = %v, want %v", got, tt.want)
				}
			}
		})
	}
}

func TestGetRoles(t *testing.T) {
	err := clearAndCreateAuthTestSetup()
	if err != nil {
		t.Fatalf("%v", err)
	}

	type args struct {
		ctx  context.Context
		addr string
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.GetRolesResponse
		wantLen int
		wantErr bool
	}{
		{
			name: "default get roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
			},
			want: &apb.GetRolesResponse{
				Status: apb.Status_STATUS_OK,
				Roles: []*apb.Role{
					{
						Name:        "adminTestRole",
						Description: "Admin",
						Permissions: []string{
							"/gosdn.core.CoreService/GetPnd",
							"/gosdn.core.CoreService/GetPndList",
						}},
					{
						Name:        "userTestRole",
						Description: "User",
						Permissions: []string{
							"/gosdn.pnd.PndService/GetChangeList",
						}},
					{
						Name:        randomRoleName,
						Description: "Not a role",
						Permissions: []string{
							"nope",
						},
					},
				},
			},
			wantLen: 3,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := GetRoles(tt.args.ctx, tt.args.addr)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetRoles() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status == tt.want.Status {
				if len(got.Roles) != 3 {
					t.Errorf("Role.GetRoles() = %v, want %v", got, tt.want)
				}
				for _, gotR := range got.Roles {
					containsExpected := false
					for _, wantR := range tt.want.Roles {
						gotPerm := gotR.Permissions
						wantPerm := wantR.Permissions
						if gotR.Description == wantR.Description && gotR.Name == wantR.Name &&
							reflect.DeepEqual(gotPerm, wantPerm) {
							containsExpected = true
							break
						}
					}
					if !containsExpected {
						t.Errorf("Role.GetRoles() = %v, want %v", got, tt.want)
					}
				}
			}
		})
	}
}

func TestUpdateRoles(t *testing.T) {
	type args struct {
		ctx   context.Context
		addr  string
		roles []*apb.Role
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.UpdateRolesResponse
		wantErr bool
	}{
		{
			name: "default update roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				roles: []*apb.Role{
					{
						Id:   adminRoleID,
						Name: "New Name",
					},
				},
			},
			want: &apb.UpdateRolesResponse{
				Status: apb.Status_STATUS_OK,
			},
			wantErr: false,
		},
		{
			name: "error update roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				roles: []*apb.Role{
					{
						Id:   uuid.NewString(),
						Name: "New Name",
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := UpdateRoles(tt.args.ctx, tt.args.addr, tt.args.roles)
			if (err != nil) != tt.wantErr {
				t.Errorf("UpdateRoles() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status != tt.want.Status {
				t.Errorf("Role.UpdateRoles() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDeletePermissionForRole(t *testing.T) {
	if err := clearAndCreateAuthTestSetup(); err != nil {
		log.Error(err)
		t.Fail()
	}

	type args struct {
		ctx                 context.Context
		addr                string
		name                string
		permissionsToDelete []string
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.DeletePermissionsForRoleResponse
		wantErr bool
	}{
		{
			name: "default delete permissions for role",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				name: "adminTestRole",
				permissionsToDelete: []string{
					"/gosdn.core.CoreService/GetPnd",
					"/gosdn.core.CoreService/GetPndList",
				},
			},
			want: &apb.DeletePermissionsForRoleResponse{
				Status: apb.Status_STATUS_OK,
			},
			wantErr: false,
		},
		{
			name: "error delete permissions for role",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				name: "adminTestRole",
				permissionsToDelete: []string{
					"foo",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := DeletePermissionForRole(tt.args.ctx, tt.args.addr, tt.args.name, tt.args.permissionsToDelete)
			if (err != nil) != tt.wantErr {
				t.Errorf("DeletePermissionForRole() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status != tt.want.Status {
				t.Errorf("Role.DeletePermissionsForRole() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDeleteRoles(t *testing.T) {
	type args struct {
		ctx      context.Context
		addr     string
		roleName []string
	}
	tests := []struct {
		name    string
		args    args
		want    *apb.DeleteRolesResponse
		wantErr bool
	}{
		{
			name: "default delete roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				roleName: []string{
					"userTestRole",
					"adminTestRole",
				},
			},
			want: &apb.DeleteRolesResponse{
				Status: apb.Status_STATUS_OK,
			},
			wantErr: false,
		},
		{
			name: "error delete roles",
			args: args{
				ctx:  context.TODO(),
				addr: testAPIEndpoint,
				roleName: []string{
					"no",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := clearAndCreateAuthTestSetup(); err != nil {
				log.Error(err)
				t.Fail()
			}

			got, err := DeleteRoles(tt.args.ctx, tt.args.addr, tt.args.roleName)
			if (err != nil) != tt.wantErr {
				t.Errorf("DeleteRoles() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if got != nil && got.Status != tt.want.Status {
				t.Errorf("Role.DeleteRoles() = %v, want %v", got, tt.want)
			}
		})
	}
}
