package main

import (
	"context"
	"strings"
	"testing"

	"github.com/nomadcode/nomadcode-core/internal/adapters/plane"
)

type mockPlaneClient struct {
	getItemFunc    func(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error)
	addCommentFunc func(ctx context.Context, input plane.AddCommentInput) error
	updateFunc     func(ctx context.Context, input plane.UpdateIssueStatusInput) error
}

func (m *mockPlaneClient) GetWorkItem(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error) {
	return m.getItemFunc(ctx, ref)
}

func (m *mockPlaneClient) AddComment(ctx context.Context, input plane.AddCommentInput) error {
	return m.addCommentFunc(ctx, input)
}

func (m *mockPlaneClient) UpdateIssueStatus(ctx context.Context, input plane.UpdateIssueStatusInput) error {
	return m.updateFunc(ctx, input)
}

func TestLoadConfig(t *testing.T) {
	tests := []struct {
		name    string
		env     map[string]string
		wantErr bool
		errMsg  string
	}{
		{
			name: "valid read-only config",
			env: map[string]string{
				"PLANE_BASE_URL":       "https://plane.example.com",
				"PLANE_TOKEN":          "secret-token",
				"PLANE_WORKSPACE_SLUG": "general",
				"PLANE_PROJECT_ID":     "proj-1",
				"PLANE_WORK_ITEM_ID":   "item-1",
			},
			wantErr: false,
		},
		{
			name: "missing required fields",
			env: map[string]string{
				"PLANE_BASE_URL": "https://plane.example.com",
			},
			wantErr: true,
			errMsg:  "missing required environment variables",
		},
		{
			name: "valid write config",
			env: map[string]string{
				"PLANE_BASE_URL":       "https://plane.example.com",
				"PLANE_TOKEN":          "secret-token",
				"PLANE_WORKSPACE_SLUG": "general",
				"PLANE_PROJECT_ID":     "proj-1",
				"PLANE_WORK_ITEM_ID":   "item-1",
				"PLANE_SMOKE_APPLY":    "1",
				"PLANE_SMOKE_COMMENT":  "test comment",
				"PLANE_SMOKE_STATE_ID": "state-done",
			},
			wantErr: false,
		},
		{
			name: "missing write fields when apply=1",
			env: map[string]string{
				"PLANE_BASE_URL":       "https://plane.example.com",
				"PLANE_TOKEN":          "secret-token",
				"PLANE_WORKSPACE_SLUG": "general",
				"PLANE_PROJECT_ID":     "proj-1",
				"PLANE_WORK_ITEM_ID":   "item-1",
				"PLANE_SMOKE_APPLY":    "1",
			},
			wantErr: true,
			errMsg:  "missing required smoke variables",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			getenv := func(key string) string {
				return tt.env[key]
			}
			cfg, err := LoadConfig(getenv)
			if (err != nil) != tt.wantErr {
				t.Fatalf("LoadConfig() error = %v, wantErr = %v", err, tt.wantErr)
			}
			if err != nil && tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) {
				t.Fatalf("expected error containing %q, got %q", tt.errMsg, err.Error())
			}
			if err == nil {
				if cfg.BaseURL != tt.env["PLANE_BASE_URL"] {
					t.Errorf("expected BaseURL %q, got %q", tt.env["PLANE_BASE_URL"], cfg.BaseURL)
				}
				if cfg.Token != tt.env["PLANE_TOKEN"] {
					t.Errorf("expected Token %q, got %q", tt.env["PLANE_TOKEN"], cfg.Token)
				}
			}
		})
	}
}

func TestRunSmoke(t *testing.T) {
	cfg := EnvConfig{
		BaseURL:       "https://plane.example.com",
		Token:         "secret-token",
		WorkspaceSlug: "general",
		ProjectID:     "proj-1",
		WorkItemID:    "item-1",
	}

	t.Run("read-only mode", func(t *testing.T) {
		var builder strings.Builder
		client := &mockPlaneClient{
			getItemFunc: func(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error) {
				return plane.WorkItem{ID: "item-1", Name: "Test Issue"}, nil
			},
		}

		err := RunSmoke(context.Background(), cfg, client, &builder)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		out := builder.String()
		if !strings.Contains(out, "lookup ok: id=item-1 name=Test Issue") {
			t.Errorf("unexpected output: %q", out)
		}
		if !strings.Contains(out, "write smoke skipped") {
			t.Errorf("expected write smoke skipped message, got %q", out)
		}
	})

	t.Run("write apply mode", func(t *testing.T) {
		writeCfg := cfg
		writeCfg.SmokeApply = true
		writeCfg.SmokeComment = "test comment"
		writeCfg.SmokeStateID = "state-done"

		var builder strings.Builder
		var commentCalled, stateCalled bool

		client := &mockPlaneClient{
			getItemFunc: func(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error) {
				return plane.WorkItem{ID: "item-1", Name: "Test Issue"}, nil
			},
			addCommentFunc: func(ctx context.Context, input plane.AddCommentInput) error {
				commentCalled = true
				if input.CommentHTML != "<p>test comment</p>" {
					t.Errorf("unexpected comment body: %q", input.CommentHTML)
				}
				return nil
			},
			updateFunc: func(ctx context.Context, input plane.UpdateIssueStatusInput) error {
				stateCalled = true
				if input.Status != "state-done" {
					t.Errorf("unexpected status: %q", input.Status)
				}
				return nil
			},
		}

		err := RunSmoke(context.Background(), writeCfg, client, &builder)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		if !commentCalled || !stateCalled {
			t.Errorf("expected write methods to be called: comment=%t, state=%t", commentCalled, stateCalled)
		}

		out := builder.String()
		if !strings.Contains(out, "lookup ok") || !strings.Contains(out, "comment ok") || !strings.Contains(out, "state update ok") {
			t.Errorf("unexpected output: %q", out)
		}
	})
}

func TestRedactToken(t *testing.T) {
	token := "super-secret-key-123"
	errStr := "api error with token super-secret-key-123 failed"
	redacted := redactToken(errStr, token)
	if strings.Contains(redacted, token) {
		t.Errorf("token was not redacted: %q", redacted)
	}
	if !strings.Contains(redacted, "<redacted>") {
		t.Errorf("redacted string should contain <redacted>: %q", redacted)
	}
}
