/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tasks

import (
	"encoding/json"
	"github.com/apache/incubator-devlake/core/errors"
	"github.com/apache/incubator-devlake/core/plugin"
	mockdal "github.com/apache/incubator-devlake/mocks/core/dal"
	mockplugin "github.com/apache/incubator-devlake/mocks/core/plugin"
	"github.com/apache/incubator-devlake/plugins/tapd/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"io/ioutil"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

// TestParseIterationChangelog tests the parseIterationChangelog function
func TestParseIterationChangelog(t *testing.T) {
	data := &TapdTaskData{Options: &TapdOptions{WorkspaceId: 1, ConnectionId: 1}}

	// Set up the required data for testing
	mockCtx := new(mockplugin.SubTaskContext)
	mockDal := new(mockdal.Dal)

	mockCtx.On("GetData").Return(data)
	mockCtx.On("GetDal").Return(mockDal)
	// Set up the required data for testing
	mockIterationFrom := &models.TapdIteration{
		ConnectionId: 1,
		Id:           1,
		Name:         "",
	}
	mockIterationTo := &models.TapdIteration{
		ConnectionId: 1,
		Id:           2,
		Name:         "",
	}
	mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
		dst := args.Get(0).(*models.TapdIteration)
		*dst = *mockIterationFrom
	}).Return(nil).Once()
	mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
		dst := args.Get(0).(*models.TapdIteration)
		*dst = *mockIterationTo
	}).Return(nil).Once()
	// Test case 2: success scenario
	iterationFromId, iterationToId, err := parseIterationChangelog(mockCtx, "old", "new")
	assert.Equal(t, int64(1), iterationFromId)
	assert.Equal(t, int64(2), iterationToId)
	assert.Nil(t, err)
}

func TestGetRawMessageDirectFromResponse(t *testing.T) {
	// Create a mock HTTP response
	body := `{"data": {"count": 10}}`
	res := &http.Response{
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(strings.NewReader(body)),
	}

	// Call the function and check the result
	rawMessages, err := GetRawMessageDirectFromResponse(res)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if len(rawMessages) != 1 {
		t.Errorf("Expected 1 raw message, got %d", len(rawMessages))
	}
	var page Page
	err = errors.Convert(json.Unmarshal(rawMessages[0], &page))
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	if page.Data.Count != 10 {
		t.Errorf("Expected count to be 10, got %d", page.Data.Count)
	}
}

func TestGetTapdTypeMappings(t *testing.T) {
	// create a mock database connection
	db := new(mockdal.Dal)
	// create some test data
	data := &TapdTaskData{
		Options: &TapdOptions{
			ConnectionId: 1,
			WorkspaceId:  2,
		},
	}

	issueTypes := make([]models.TapdWorkitemType, 0)
	issueTypes = append(issueTypes, models.TapdWorkitemType{
		ConnectionId: 1,
		WorkspaceId:  2,
		Id:           1,
		Name:         "Story",
	})
	issueTypes = append(issueTypes, models.TapdWorkitemType{
		ConnectionId: 1,
		WorkspaceId:  2,
		Id:           2,
		Name:         "Bug",
	})
	db.On("All", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
		dst := args.Get(0).(*[]models.TapdWorkitemType)
		*dst = issueTypes
	}).Return(nil).Once()
	// call the function being tested
	result, err := getTapdTypeMappings(data, db, "story")

	// check if the result is correct
	if err != nil {
		t.Errorf("getTapdTypeMappings returned an error: %v", err)
	}
	if len(result) != 2 {
		t.Errorf("getTapdTypeMappings returned %d items, expected 2", len(result))
	}
	if result[1] != "Story" {
		t.Errorf("getTapdTypeMappings returned incorrect value for ID 1: %s", result[1])
	}
	if result[2] != "Bug" {
		t.Errorf("getTapdTypeMappings returned incorrect value for ID 2: %s", result[2])
	}
}

// test case for when the status list is empty
func TestGetDefaultStdStatusMappingEmptyStatusList(t *testing.T) {
	data := &TapdTaskData{
		Options: &TapdOptions{
			ConnectionId: 123,
			WorkspaceId:  456,
		},
	}
	db := new(mockdal.Dal)
	statusList := []models.TapdStoryStatus{
		{
			ConnectionId: 123,
			WorkspaceId:  456,
			EnglishName:  "Done",
			ChineseName:  "已完成",
			IsLastStep:   true,
		},
		{
			ConnectionId: 123,
			WorkspaceId:  456,
			EnglishName:  "In Progress",
			ChineseName:  "进行中",
		},
	}
	db.On("All", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
		dst := args.Get(0).(*[]models.TapdStoryStatus)
		*dst = statusList
	}).Return(nil).Once()
	statusLanguageMap, getStdStatus, err := getDefaultStdStatusMapping(data, db, statusList)
	if err != nil {
		t.Errorf("getDefaultStdStatusMapping returned an error: %v", err)
	}
	expectedStatusLanguageMap := map[string]string{
		"Done":        "已完成",
		"In Progress": "进行中",
	}
	if !reflect.DeepEqual(statusLanguageMap, expectedStatusLanguageMap) {
		t.Errorf("getDefaultStdStatusMapping returned unexpected statusLanguageMap: got %v, want %v", statusLanguageMap, expectedStatusLanguageMap)
	}
	expectedGetStdStatus := map[string]string{
		"已完成": "DONE",
		"进行中": "IN_PROGRESS",
	}
	for k, v := range expectedGetStdStatus {
		if getStdStatus(k) != v {
			t.Errorf("getDefaultStdStatusMapping returned unexpected getStdStatus for %v: got %v, want %v", k, getStdStatus(k), v)
		}
	}
}

func TestUnicodeToZh(t *testing.T) {
	input := "\\u4e2d\\u6587"
	expected := "中文"
	output, err := unicodeToZh(input)
	if err != nil {
		t.Errorf("unicodeToZh(%q) returned error %v", input, err)
	}
	if output != expected {
		t.Errorf("unicodeToZh(%q) = %q, want %q", input, output, expected)
	}
}

func TestConvertUnicode(t *testing.T) {
	testStruct := struct {
		ValueBeforeParsed string
		ValueAfterParsed  string
	}{
		ValueBeforeParsed: "Hello, \\u4e16\\u754c!",
		ValueAfterParsed:  "--",
	}

	err := convertUnicode(&testStruct)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	expectedBefore := "Hello, 世界!"
	if testStruct.ValueBeforeParsed != expectedBefore {
		t.Errorf("Expected ValueBeforeParsed to be %q, but got %q", expectedBefore, testStruct.ValueBeforeParsed)
	}

	expectedAfter := ""
	if testStruct.ValueAfterParsed != expectedAfter {
		t.Errorf("Expected ValueAfterParsed to be %q, but got %q", expectedAfter, testStruct.ValueAfterParsed)
	}
}

func TestGenerateDomainAccountIdForUsers(t *testing.T) {
	connectionId := uint64(123)
	testCases := []struct {
		param    string
		expected string
	}{
		{"user1,user2,user3", "tapd:TapdAccount:123:user1,tapd:TapdAccount:123:user2,tapd:TapdAccount:123:user3"},
		{"user4;user5;user6", "tapd:TapdAccount:123:user4,tapd:TapdAccount:123:user5,tapd:TapdAccount:123:user6"},
		{"", ""},
	}
	mockMeta := mockplugin.NewPluginMeta(t)
	mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/tapd")
	mockMeta.On("Name").Return("tapd").Maybe()
	err := plugin.RegisterPlugin("tapd", mockMeta)
	assert.Nil(t, err)
	for _, testCase := range testCases {
		result := generateDomainAccountIdForUsers(testCase.param, connectionId)
		if result != testCase.expected {
			t.Errorf("generateDomainAccountIdForUsers(%s, %d) = %s; expected %s", testCase.param, connectionId, result, testCase.expected)
		}
	}
}

func Test_extractStatus(t *testing.T) {
	type args struct {
		blob []byte
	}
	tests := []struct {
		name  string
		args  args
		want  map[string]string
		want1 errors.Error
	}{
		{
			"non-empty array",
			args{
				blob: []byte(`{"data":[{"id":1,"name":"已完成","is_last_step":true},{"id":2,"name":"进行中"}],"status":1,"message":"success"}`),
			},
			nil,
			nil,
		},
		{
			"empty array",
			args{
				blob: []byte(`{"status":1,"data":[],"info":"success"}`),
			},
			nil,
			nil,
		},
		{
			"object",
			args{
				blob: []byte(`{"status":1,"data":{"new":"新建","in_progress":"开发处理"},"info":"success"}`),
			},
			map[string]string{"new": "新建", "in_progress": "开发处理"},
			nil,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := extractStatus(tt.args.blob)
			assert.Equalf(t, tt.want, got, "extractStatus(%v)", tt.args.blob)
			assert.Equalf(t, tt.want1, got1, "extractStatus(%v)", tt.args.blob)
		})
	}
}
