package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/store"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host", Options: rpservice.TargetOptions{DirectUpstream: true}},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
				{
					ID:           "svc3-private",
					Enabled:      true,
					Private:      true,
					AccessGroups: []string{"grp-eng", "grp-ops"},
					Targets: []*rpservice.Target{
						{TargetType: "cluster", Options: rpservice.TargetOptions{DirectUpstream: true}},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// GetProxyMetrics returns canned proxy/cluster counts so the
// generateProperties test can assert the BYOP signals end-to-end.
func (mockDatasource) GetProxyMetrics(_ context.Context) (store.ProxyMetrics, error) {
	return store.ProxyMetrics{
		Clusters:         3,
		ClustersBYOP:     1,
		ClustersPrivate:  1,
		Proxies:          4,
		ProxiesConnected: 2,
	}, nil
}

// GetAgentNetworkMetrics returns canned agent-network counts so the
// generateProperties test can assert the adoption/usage signals end-to-end.
func (mockDatasource) GetAgentNetworkMetrics(_ context.Context) (store.AgentNetworkMetrics, error) {
	return store.AgentNetworkMetrics{
		Accounts:             2,
		Providers:            5,
		Policies:             3,
		BudgetRules:          1,
		LogCollectionEnabled: 2,
		InputTokens:          1000,
		OutputTokens:         500,
		CostUSD:              1.25,
	}, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 2 {
		t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_users_oidc"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_oidc, got %v", properties["embedded_idp_users_oidc"])
	}
	if properties["embedded_idp_count"] != 3 {
		t.Errorf("expected 3 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 3 {
		t.Errorf("expected 3 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 2 {
		t.Errorf("expected 2 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 4 {
		t.Errorf("expected 4 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 2 {
		t.Errorf("expected 2 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_target_type_cluster"] != 1 {
		t.Errorf("expected 1 services_target_type_cluster, got %v", properties["services_target_type_cluster"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["services_private"] != 1 {
		t.Errorf("expected 1 services_private, got %v", properties["services_private"])
	}
	if properties["services_private_with_access_groups"] != 1 {
		t.Errorf("expected 1 services_private_with_access_groups, got %v", properties["services_private_with_access_groups"])
	}
	if properties["services_private_access_groups_sum"] != 2 {
		t.Errorf("expected 2 services_private_access_groups_sum, got %v", properties["services_private_access_groups_sum"])
	}
	if properties["services_with_direct_upstream"] != 2 {
		t.Errorf("expected 2 services_with_direct_upstream, got %v", properties["services_with_direct_upstream"])
	}
	if properties["proxy_clusters"] != int64(3) {
		t.Errorf("expected 3 proxy_clusters, got %v", properties["proxy_clusters"])
	}
	if properties["proxy_clusters_byop"] != int64(1) {
		t.Errorf("expected 1 proxy_clusters_byop, got %v", properties["proxy_clusters_byop"])
	}
	if properties["proxy_clusters_private"] != int64(1) {
		t.Errorf("expected 1 proxy_clusters_private, got %v", properties["proxy_clusters_private"])
	}
	if properties["proxies"] != int64(4) {
		t.Errorf("expected 4 proxies, got %v", properties["proxies"])
	}
	if properties["proxies_connected"] != int64(2) {
		t.Errorf("expected 2 proxies_connected, got %v", properties["proxies_connected"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}
