// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package print

import (
	"strings"
	"testing"
	"time"

	"code.gitea.io/sdk/gitea"
	"github.com/stretchr/testify/assert"
)

func TestWebhooksList(t *testing.T) {
	now := time.Now()

	hooks := []*gitea.Hook{
		{
			ID:   1,
			Type: "gitea",
			Config: map[string]string{
				"url": "https://example.com/webhook",
			},
			Events:  []string{"push", "pull_request"},
			Active:  true,
			Updated: now,
		},
		{
			ID:   2,
			Type: "slack",
			Config: map[string]string{
				"url": "https://hooks.slack.com/services/xxx",
			},
			Events:  []string{"push"},
			Active:  false,
			Updated: now,
		},
		{
			ID:      3,
			Type:    "discord",
			Config:  nil,
			Events:  []string{"pull_request", "pull_request_review_approved"},
			Active:  true,
			Updated: now,
		},
	}

	// Test that function doesn't panic with various output formats
	outputFormats := []string{"table", "csv", "json", "yaml", "simple", "tsv"}

	for _, format := range outputFormats {
		t.Run("Format_"+format, func(t *testing.T) {
			assert.NoError(t, WebhooksList(hooks, format))
		})
	}
}

func TestWebhooksListEmpty(t *testing.T) {
	// Test with empty hook list
	hooks := []*gitea.Hook{}

	assert.NoError(t, WebhooksList(hooks, "table"))
}

func TestWebhooksListNil(t *testing.T) {
	// Test with nil hook list
	assert.NoError(t, WebhooksList(nil, "table"))
}

func TestWebhookDetails(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name string
		hook *gitea.Hook
	}{
		{
			name: "Complete webhook",
			hook: &gitea.Hook{
				ID:   123,
				Type: "gitea",
				Config: map[string]string{
					"url":                  "https://example.com/webhook",
					"content_type":         "json",
					"http_method":          "post",
					"branch_filter":        "main,develop",
					"secret":               "secret-value",
					"authorization_header": "Bearer token123",
				},
				Events:  []string{"push", "pull_request", "issues"},
				Active:  true,
				Created: now.Add(-24 * time.Hour),
				Updated: now,
			},
		},
		{
			name: "Minimal webhook",
			hook: &gitea.Hook{
				ID:      456,
				Type:    "slack",
				Config:  map[string]string{"url": "https://hooks.slack.com/xxx"},
				Events:  []string{"push"},
				Active:  false,
				Created: now,
				Updated: now,
			},
		},
		{
			name: "Webhook with nil config",
			hook: &gitea.Hook{
				ID:      789,
				Type:    "discord",
				Config:  nil,
				Events:  []string{"pull_request"},
				Active:  true,
				Created: now,
				Updated: now,
			},
		},
		{
			name: "Webhook with empty config",
			hook: &gitea.Hook{
				ID:      999,
				Type:    "gitea",
				Config:  map[string]string{},
				Events:  []string{},
				Active:  false,
				Created: now,
				Updated: now,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Should not panic
			assert.NotPanics(t, func() {
				WebhookDetails(tt.hook)
			})
		})
	}
}

func TestWebhookEventsTruncation(t *testing.T) {
	tests := []struct {
		name           string
		events         []string
		maxLength      int
		shouldTruncate bool
	}{
		{
			name:           "Short events list",
			events:         []string{"push"},
			maxLength:      40,
			shouldTruncate: false,
		},
		{
			name:           "Medium events list",
			events:         []string{"push", "pull_request"},
			maxLength:      40,
			shouldTruncate: false,
		},
		{
			name:           "Long events list",
			events:         []string{"push", "pull_request", "pull_request_review_approved", "pull_request_sync", "issues"},
			maxLength:      40,
			shouldTruncate: true,
		},
		{
			name:           "Very long events list",
			events:         []string{"push", "pull_request", "pull_request_review_approved", "pull_request_review_rejected", "pull_request_comment", "pull_request_assigned", "pull_request_label"},
			maxLength:      40,
			shouldTruncate: true,
		},
		{
			name:           "Empty events",
			events:         []string{},
			maxLength:      40,
			shouldTruncate: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			eventsStr := strings.Join(tt.events, ",")

			if len(eventsStr) > tt.maxLength {
				assert.True(t, tt.shouldTruncate, "Events string should be truncated")
				truncated := eventsStr[:tt.maxLength-3] + "..."
				assert.Contains(t, truncated, "...")
				assert.LessOrEqual(t, len(truncated), tt.maxLength)
			} else {
				assert.False(t, tt.shouldTruncate, "Events string should not be truncated")
			}
		})
	}
}

func TestWebhookActiveStatus(t *testing.T) {
	tests := []struct {
		name           string
		active         bool
		expectedSymbol string
	}{
		{
			name:           "Active webhook",
			active:         true,
			expectedSymbol: "✓",
		},
		{
			name:           "Inactive webhook",
			active:         false,
			expectedSymbol: "✗",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			symbol := "✓"
			if !tt.active {
				symbol = "✗"
			}

			assert.Equal(t, tt.expectedSymbol, symbol)
		})
	}
}

func TestWebhookConfigHandling(t *testing.T) {
	tests := []struct {
		name          string
		config        map[string]string
		expectedURL   string
		hasSecret     bool
		hasAuthHeader bool
	}{
		{
			name: "Config with all fields",
			config: map[string]string{
				"url":                  "https://example.com/webhook",
				"secret":               "my-secret",
				"authorization_header": "Bearer token",
				"content_type":         "json",
				"http_method":          "post",
				"branch_filter":        "main",
			},
			expectedURL:   "https://example.com/webhook",
			hasSecret:     true,
			hasAuthHeader: true,
		},
		{
			name: "Config with minimal fields",
			config: map[string]string{
				"url": "https://hooks.slack.com/xxx",
			},
			expectedURL:   "https://hooks.slack.com/xxx",
			hasSecret:     false,
			hasAuthHeader: false,
		},
		{
			name:          "Nil config",
			config:        nil,
			expectedURL:   "",
			hasSecret:     false,
			hasAuthHeader: false,
		},
		{
			name:          "Empty config",
			config:        map[string]string{},
			expectedURL:   "",
			hasSecret:     false,
			hasAuthHeader: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var url string
			if tt.config != nil {
				url = tt.config["url"]
			}
			assert.Equal(t, tt.expectedURL, url)

			var hasSecret, hasAuthHeader bool
			if tt.config != nil {
				_, hasSecret = tt.config["secret"]
				_, hasAuthHeader = tt.config["authorization_header"]
			}
			assert.Equal(t, tt.hasSecret, hasSecret)
			assert.Equal(t, tt.hasAuthHeader, hasAuthHeader)
		})
	}
}

func TestWebhookTableHeaders(t *testing.T) {
	expectedHeaders := []string{
		"ID",
		"Type",
		"URL",
		"Events",
		"Active",
		"Updated",
	}

	// Verify all headers are non-empty and unique
	headerSet := make(map[string]bool)
	for _, header := range expectedHeaders {
		assert.NotEmpty(t, header, "Header should not be empty")
		assert.False(t, headerSet[header], "Header %s should be unique", header)
		headerSet[header] = true
	}

	assert.Len(t, expectedHeaders, 6, "Should have exactly 6 headers")
}

func TestWebhookTypeValues(t *testing.T) {
	validTypes := []string{
		"gitea",
		"gogs",
		"slack",
		"discord",
		"dingtalk",
		"telegram",
		"msteams",
		"feishu",
		"wechatwork",
		"packagist",
	}

	for _, hookType := range validTypes {
		t.Run("Type_"+hookType, func(t *testing.T) {
			assert.NotEmpty(t, hookType, "Hook type should not be empty")
		})
	}
}

func TestWebhookDetailsFormatting(t *testing.T) {
	now := time.Now()
	hook := &gitea.Hook{
		ID:   123,
		Type: "gitea",
		Config: map[string]string{
			"url":                  "https://example.com/webhook",
			"content_type":         "json",
			"http_method":          "post",
			"branch_filter":        "main,develop",
			"secret":               "secret-value",
			"authorization_header": "Bearer token123",
		},
		Events:  []string{"push", "pull_request", "issues"},
		Active:  true,
		Created: now.Add(-24 * time.Hour),
		Updated: now,
	}

	// Test that all expected fields are included in details
	expectedElements := []string{
		"123",                         // webhook ID
		"gitea",                       // webhook type
		"true",                        // active status
		"https://example.com/webhook", // URL
		"json",                        // content type
		"post",                        // HTTP method
		"main,develop",                // branch filter
		"(configured)",                // secret indicator
		"(configured)",                // auth header indicator
		"push, pull_request, issues",  // events list
	}

	// Verify elements exist (placeholder test)
	assert.Greater(t, len(expectedElements), 0, "Should have expected elements")

	// This is a functional test - in practice, we'd capture output
	// For now, we verify the webhook structure contains expected data
	assert.Equal(t, int64(123), hook.ID)
	assert.Equal(t, "gitea", hook.Type)
	assert.True(t, hook.Active)
	assert.Equal(t, "https://example.com/webhook", hook.Config["url"])
	assert.Equal(t, "json", hook.Config["content_type"])
	assert.Equal(t, "post", hook.Config["http_method"])
	assert.Equal(t, "main,develop", hook.Config["branch_filter"])
	assert.Contains(t, hook.Config, "secret")
	assert.Contains(t, hook.Config, "authorization_header")
	assert.Equal(t, []string{"push", "pull_request", "issues"}, hook.Events)
}
