package oauth2

import (
	"context"
	"testing"

	"github.com/tidbcloud/auth-server/internal/entity"
)

func TestValidateRedirectURI(t *testing.T) {
	cases := []struct {
		name           string
		appRedirectURI string
		reqRedirectURI string
		isValid        bool
	}{
		{
			name:           "[http]redirect uri is valid",
			appRedirectURI: "http://localhost:8080",
			reqRedirectURI: "http://localhost:8080",
			isValid:        true,
		},

		{
			name:           "[http]redirect uri is valid",
			appRedirectURI: "http://localhost:8080,http://127.0.0.1:8080,",
			reqRedirectURI: "http://localhost:8080",
			isValid:        true,
		},

		{
			name:           "[http]redirect uri is valid with path",
			appRedirectURI: "http://localhost:8080/path",
			reqRedirectURI: "http://localhost:8080/path",
			isValid:        true,
		},
		{
			name:           "[http]redirect uri is valid with path",
			appRedirectURI: "http://localhost:8080/path,http://localhost:8080/path2",
			reqRedirectURI: "http://localhost:8080/path",
			isValid:        true,
		},

		{
			name:           "[http]redirect uri is valid with query",
			appRedirectURI: "http://localhost:8080",
			reqRedirectURI: "http://localhost:8080?query=1",
			isValid:        true,
		},
		{
			name:           "[http]redirect uri is valid with path and query",
			appRedirectURI: "http://localhost:8080/path?query=1",
			reqRedirectURI: "http://localhost:8080/path?query=1",
			isValid:        true,
		},
		{
			name:           "[http]redirect uri is valid with fragment",
			appRedirectURI: "http://localhost:8080/path",
			reqRedirectURI: "http://localhost:8080/path#fragment",
			isValid:        true,
		},
		{
			name:           "[http]redirect uri is valid with query and fragment",
			appRedirectURI: "http://localhost:8080/path",
			reqRedirectURI: "http://localhost:8080/path?q=xyz#fragment",
			isValid:        true,
		},
		{
			name:           "[https]redirect uri is valid",
			appRedirectURI: "https://example.com",
			reqRedirectURI: "https://example.com",
			isValid:        true,
		},
		{
			name:           "[https]redirect uri is valid with path",
			appRedirectURI: "https://example.com/path",
			reqRedirectURI: "https://example.com/path",
			isValid:        true,
		},
		{
			name:           "[https]redirect uri is invalid with subpath",
			appRedirectURI: "https://example.com/path",
			reqRedirectURI: "https://example.com/path/subpath",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with subpath 2",
			appRedirectURI: "https://example.com/path",
			reqRedirectURI: "https://example.com/path1",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with different scheme",
			appRedirectURI: "https://example.com",
			reqRedirectURI: "http://example.com",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with different port",
			appRedirectURI: "https://example.com",
			reqRedirectURI: "https://example.com:8080",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with different host",
			appRedirectURI: "https://example.com",
			reqRedirectURI: "https://example2.com",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with different path",
			appRedirectURI: "https://example.com/path",
			reqRedirectURI: "https://example.com/1path",
			isValid:        false,
		},
		{
			name:           "[https]redirect uri is invalid with relative path",
			appRedirectURI: "https://example.com/path",
			reqRedirectURI: "/1path",
			isValid:        false,
		},

		{
			name:           "[https]redirect uri is invalid with relative path",
			appRedirectURI: "https://example.com/path,https://example.com/path44",
			reqRedirectURI: "/1path",
			isValid:        false,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			_, err := validateRedirectURI(tc.appRedirectURI, tc.reqRedirectURI)
			if tc.isValid && err != nil {
				t.Errorf("expected no error, but got %v", err)
			}
			if !tc.isValid && err == nil {
				t.Errorf("expected error, but got nil")
			}
		})
	}

}

func TestValidateScope(t *testing.T) {
	ctx := context.Background()
	cases := []struct {
		name        string
		appScope    entity.Oauth2Scope
		reqScopes   []string
		orgRole     string
		projectRole string
		isValid     bool
	}{
		{
			name:        "valid scope",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"org:member", "project:owner"},
			orgRole:     "org:member",
			projectRole: "project:owner",
			isValid:     true,
		},
		{
			name:        "valid minor scope case 1",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"org:member", "project:dev"},
			orgRole:     "org:member",
			projectRole: "project:dev",
			isValid:     true,
		},
		{
			name:        "valid minor scope case 2",
			appScope:    entity.Oauth2Scope{Org: "org:owner", Project: "project:owner"},
			reqScopes:   []string{"org:member", "project:dev"},
			orgRole:     "org:member",
			projectRole: "project:dev",
			isValid:     true,
		},
		{
			name:        "empty req scope",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{},
			orgRole:     "org:member",
			projectRole: "project:owner",
			isValid:     true,
		},
		{
			name:        "invalid scope case 1",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"org:member"},
			orgRole:     "org:member",
			projectRole: "",
			isValid:     true,
		},
		{
			name:        "invalid scope case 2",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"project:dev"},
			orgRole:     "",
			projectRole: "",
			isValid:     false,
		},
		{
			name:        "invalid scope case 3",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"org:owner", "project:owner"},
			orgRole:     "",
			projectRole: "",
			isValid:     false,
		},
		{
			name:        "invalid scope case 3",
			appScope:    entity.Oauth2Scope{Org: "org:member", Project: "project:owner"},
			reqScopes:   []string{"invalid"},
			orgRole:     "",
			projectRole: "",
			isValid:     false,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			orgRole, projectRole, err := validateScope(ctx, tc.appScope, tc.reqScopes)
			if tc.isValid {
				if err != nil {
					t.Errorf("expected no error, but got %v", err)
				}
				if orgRole != tc.orgRole {
					t.Errorf("expected org role %s, but got %s", tc.orgRole, orgRole)
				}
				if projectRole != tc.projectRole {
					t.Errorf("expected project role %s, but got %s", tc.projectRole, projectRole)
				}
			}
			if !tc.isValid && err == nil {
				t.Errorf("expected error, but got nil")
			}
		})
	}
}
