package processor

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go-v2/aws"
	s3api "github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	mock_data_processor "github.com/tidbcloud/observability/data-collector/test/mockdataprocessor"
)

func TestDataProcessor_ProcessCleanedData(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		prepareData("test", "ossinsight", "v1", "test_explorer_questions.csv", [][]string{
			{"id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "ossinsight", "v1")
		assert.NoError(t, err)
	})

	t.Run("write error", func(t *testing.T) {
		WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
				return WriteCsvFile(writer, content)
			}
		}()
		prepareData("test", "ossinsight", "v1", "test_explorer_questions.csv", [][]string{
			{"id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "ossinsight", "v1")
		assert.Error(t, err)
	})

	t.Run("read error", func(t *testing.T) {
		readFileToMapFunc = func(path string, mp map[string]map[string]string, selectedID string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			readFileToMapFunc = func(path string, mp map[string]map[string]string, selectedID string) error {
				return readFileToMap(path, mp, selectedID)
			}
		}()
		prepareData("test", "ossinsight", "v1", "test_explorer_questions.csv", [][]string{
			{"id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "ossinsight", "v1")
		assert.Error(t, err)
	})

	t.Run("delete error", func(t *testing.T) {
		DeleteFilesFunc = func(pattern string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			DeleteFilesFunc = func(pattern string) error {
				return DeleteFiles(pattern)
			}
		}()
		prepareData("test", "ossinsight", "v1", "test_explorer_questions.csv", [][]string{
			{"id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "ossinsight", "v1")
		assert.NoError(t, err)
	})

	t.Run("chat2query success", func(t *testing.T) {
		prepareData("test", "chat2query", "v1", "test_explorer_questions.csv", [][]string{
			{"session_id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "chat2query", "v1")
		assert.NoError(t, err)
	})

	t.Run("chat2query read err", func(t *testing.T) {
		prepareData("test", "chat2query", "v1", "test_explorer_questions.csv", [][]string{
			{"session_id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		readFileToMapFunc = func(path string, mp map[string]map[string]string, selectedID string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			readFileToMapFunc = func(path string, mp map[string]map[string]string, selectedID string) error {
				return readFileToMap(path, mp, selectedID)
			}
		}()

		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "chat2query", "v1")
		assert.Error(t, err)
	})

	t.Run("chat2query delete err", func(t *testing.T) {
		prepareData("test", "chat2query", "v1", "test_explorer_questions.csv", [][]string{
			{"session_id", "title", "status", "user_id", "query_sql", "result", "created_at"},
			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
		})
		defer deleteData("test")
		DeleteFilesFunc = func(pattern string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			DeleteFilesFunc = func(pattern string) error {
				return DeleteFiles(pattern)
			}
		}()

		ctl := gomock.NewController(t)
		s3 := mock_data_processor.NewMocks3Client(ctl)
		mockS3 := newMockS3Client(s3)
		processor := newMockDataProcessor(mockS3)
		err := processor.ProcessCleanedData("test", "test", "chat2query", "v1")
		assert.NoError(t, err)
	})

}

func TestDataProcessor_MergeProcessedData(t *testing.T) {
	ctl := gomock.NewController(t)
	s3 := mock_data_processor.NewMocks3Client(ctl)
	mockS3 := newMockS3Client(s3)
	processor := newMockDataProcessor(mockS3)
	t.Run("success", func(t *testing.T) {
		s3.EXPECT().ListObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(&s3api.ListObjectsOutput{
			Contents: []types.Object{
				{
					Key: aws.String("20230308-timestamp.csv"),
				},
			},
			Prefix: aws.String("test/data"),
		}, nil).Times(1)
		buf := []byte("question_id,user_id,title,status,query_sql,result,result_type,feedback_type,feedback_content,created_at\nid,id,title,success,select * from table,,empty,,,\nid1,id1,title1,success,select * from table1,null,null,,,")
		tmp := CBuffer{bytes.NewBuffer(buf)}

		s3.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(&s3api.GetObjectOutput{
			Body:          &tmp,
			ContentLength: int64(len(buf)),
		}, nil).Times(1)

		err := processor.MergeProcessedData("test", "test", "ossinsight", "v1")
		defer deleteData("./test")
		assert.NoError(t, err)
	})
	t.Run("download error", func(t *testing.T) {
		s3.EXPECT().ListObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1)

		err := processor.MergeProcessedData("test", "test", "ossinsight", "v1")
		assert.Error(t, err)
	})
	t.Run("download error", func(t *testing.T) {
		s3.EXPECT().ListObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(&s3api.ListObjectsOutput{
			Contents: []types.Object{
				{
					Key: aws.String("20230308-timestamp.csv"),
				},
			},
			Prefix: aws.String("test/data"),
		}, nil).Times(1)

		s3.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1).Times(1)

		err := processor.MergeProcessedData("test", "test", "ossinsight", "v1")
		assert.Error(t, err)
	})
	t.Run("write err", func(t *testing.T) {
		WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
			return fmt.Errorf("error")
		}
		defer func() {
			WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
				return WriteCsvFile(writer, content)
			}
		}()

		s3.EXPECT().ListObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(&s3api.ListObjectsOutput{
			Contents: []types.Object{
				{
					Key: aws.String("20230308-timestamp.csv"),
				},
			},
			Prefix: aws.String("test/data"),
		}, nil).Times(1)
		buf := []byte("question_id,user_id,title,status,query_sql,result,result_type,feedback_type,feedback_content,created_at\nid,id,title,success,select * from table,,empty,,,\nid1,id1,title1,success,select * from table1,null,null,,,")
		tmp := CBuffer{bytes.NewBuffer(buf)}

		s3.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(&s3api.GetObjectOutput{
			Body:          &tmp,
			ContentLength: int64(len(buf)),
		}, nil).Times(1)

		err := processor.MergeProcessedData("test", "test", "ossinsight", "v1")
		defer deleteData("./test")
		assert.Error(t, err)
	})

}

func Test_writeFileWithinMaxSize(t *testing.T) {
	err := writeFileWithinMaxSize("test/", "default", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 0, "20230306", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 50)
	assert.NoError(t, err)
	err = writeFileWithinMaxSize("test/", "default", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 0, "20230306", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 50)
	assert.NoError(t, err)
	defer deleteData("test")

	WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
		return fmt.Errorf("error")
	}
	defer func() {
		WriteCsvFileFunc = func(writer *csv.Writer, content []string) error {
			return WriteCsvFile(writer, content)
		}
	}()

	err = writeFileWithinMaxSize("test/", "default", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 0, "20230306", []string{"ttttt", "wwwww", "yyyy", "uuuu"}, 50)
	assert.Error(t, err)

}

//func Test_readFileToMapChat2Query(t *testing.T) {
//	t.Run("success", func(t *testing.T) {
//		prepareData("test", "chat2query", "v1", "test_explorer_questions.csv", [][]string{
//			{"session_id", "title", "status", "user_id", "query_sql", "result", "created_at"},
//			{"f5b3171a4f547b2b0aee9c327b92b3f", "bạn là ai", "error", "1170032", "null", "", "2023-03-07 12:21:42"},
//		})
//		defer deleteData("test")
//		var records = make(map[string]map[string]string)
//		err := readFileToMapChat2Query("./test/chat2query/v1/*.csv", records)
//		assert.NoError(t, err)
//	})
//
//}
