package database

import (
	"context"
	"fmt"
	"reflect"
	"testing"
	"time"

	"git.anthrove.art/Anthrove/otter-space-sdk/v6/pkg/models"
	"git.anthrove.art/Anthrove/otter-space-sdk/v6/test"
	"go.opentelemetry.io/contrib/bridges/otellogrus"
	"go.opentelemetry.io/otel"
	"gorm.io/gorm"
)

func TestCreatePostReport(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PostgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests

	// -- Create Post to test with
	validPost := models.Post{
		BaseModel: models.BaseModel[models.PostID]{
			ID:        models.PostID(fmt.Sprintf("%025s", "Post1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Rating: models.SFW,
	}

	validPost, err = CreatePost(ctx, validPost)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// Setup Tests
	validUser01 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User1"))}}
	validUser01, err = CreateUser(ctx, validUser01)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	validUser02 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User2"))}}
	validUser02, err = CreateUser(ctx, validUser02)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// -- Create PostReport to test with
	validPostReport := models.PostReport{
		BaseModel: models.BaseModel[models.PostReportID]{
			ID:        models.PostReportID(fmt.Sprintf("%025s", "PostReport1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		PostID:            validPost.ID,
		ReportBy:          validUser01.ID,
		ReportDescription: "aaa",
		AuditBy:           &validUser02.ID,
		AuditDescription:  "ahaa",
		ReportType:        models.Duplicate,
		ReportState:       models.Rejected,
	}
	// --

	// -- -- Tests
	type args struct {
		ctx    context.Context
		source models.PostReport
	}
	tests := []struct {
		name    string
		args    args
		want    models.PostReport
		wantErr bool
	}{
		{
			name: "Test 01: Valid PostReport",
			args: args{
				ctx:    ctx,
				source: validPostReport,
			},
			want:    validPostReport,
			wantErr: false,
		},
		{
			name: "Test 02: Duplicate PostReport",
			args: args{
				ctx:    ctx,
				source: validPostReport,
			},
			want:    models.PostReport{},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := CreatePostReport(tt.args.ctx, tt.args.source)
			if (err != nil) != tt.wantErr {
				t.Errorf("CreatePostReport() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CreatePostReport() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestUpdatePostReport(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PostgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- Create Post to test with
	validPost := models.Post{
		BaseModel: models.BaseModel[models.PostID]{
			ID:        models.PostID(fmt.Sprintf("%025s", "Post1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Rating: models.SFW,
	}

	validPost, err = CreatePost(ctx, validPost)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// Setup Tests
	validUser01 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User1"))}}
	validUser01, err = CreateUser(ctx, validUser01)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	validUser02 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User2"))}}
	validUser02, err = CreateUser(ctx, validUser02)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// -- Create PostReport to test with
	validPostReport := models.PostReport{
		BaseModel: models.BaseModel[models.PostReportID]{
			ID:        models.PostReportID(fmt.Sprintf("%025s", "PostReport1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		PostID:            validPost.ID,
		ReportBy:          validUser01.ID,
		ReportDescription: "aaa",
		AuditBy:           nil,
		AuditDescription:  "",
		ReportType:        models.Duplicate,
		ReportState:       models.Rejected,
	}
	// --

	validPostReport, err = CreatePostReport(ctx, validPostReport)
	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// --

	// -- Create Updates models for UserPostReport
	validUpdatePostReport := validPostReport
	validUpdatePostReport.AuditBy = &validUser02.ID
	validUpdatePostReport.AuditDescription = "aaaaa"
	validUpdatePostReport.ReportState = models.Approved

	invalidUpdatePostReport := models.PostReport{}
	// --

	// -- -- Tests
	type args struct {
		ctx    context.Context
		source models.PostReport
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "Test 01: Valid Update for PostReport",
			args: args{
				ctx:    ctx,
				source: validUpdatePostReport,
			},
			wantErr: false,
		},
		{
			name: "Test 02: Invalid Update for PostReport",
			args: args{
				ctx:    ctx,
				source: invalidUpdatePostReport,
			},
			wantErr: true,
		},
		{
			name: "Test 03: Empty ID for Update for PostReport",
			args: args{
				ctx:    ctx,
				source: models.PostReport{BaseModel: models.BaseModel[models.PostReportID]{ID: ""}},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := UpdatePostReport(tt.args.ctx, tt.args.source); (err != nil) != tt.wantErr {
				t.Errorf("UpdatePostReport() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestGetPostReportByID(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PostgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests
	// -- Create Post to test with
	validPost := models.Post{
		BaseModel: models.BaseModel[models.PostID]{
			ID:        models.PostID(fmt.Sprintf("%025s", "Post1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Rating: models.SFW,
	}

	validPost, err = CreatePost(ctx, validPost)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// Setup Tests
	validUser01 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User1"))}}
	validUser01, err = CreateUser(ctx, validUser01)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	validUser02 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User2"))}}
	validUser02, err = CreateUser(ctx, validUser02)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// -- Create PostReport to test with
	validPostReport := models.PostReport{
		BaseModel: models.BaseModel[models.PostReportID]{
			ID:        models.PostReportID(fmt.Sprintf("%025s", "PostReport1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		PostID:            validPost.ID,
		ReportBy:          validUser01.ID,
		ReportDescription: "aaa",
		AuditBy:           nil,
		AuditDescription:  "",
		ReportType:        models.Duplicate,
		ReportState:       models.Rejected,
	}
	// --

	validPostReport, err = CreatePostReport(ctx, validPostReport)
	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// --
	// --

	// -- -- Tests
	type args struct {
		ctx context.Context
		id  models.PostReportID
	}
	tests := []struct {
		name    string
		args    args
		want    models.PostReport
		wantErr bool
	}{
		{
			name: "Test 01: Valid PostReport ID",
			args: args{
				ctx: ctx,
				id:  validPostReport.ID,
			},
			want:    validPostReport,
			wantErr: false,
		},
		{
			name: "Test 03: Empty PostReportID",
			args: args{
				ctx: ctx,
				id:  "",
			},
			wantErr: true,
		},
		{
			name: "Test 04: Short PostReportID",
			args: args{
				ctx: ctx,
				id:  "111",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := GetPostReportByID(tt.args.ctx, tt.args.id)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetPostReportByID() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !checkPostReportID(got, tt.want) {
				t.Errorf("GetPostReportByID() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDeletePostReport(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PostgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests
	// -- Create Post to test with
	validPost := models.Post{
		BaseModel: models.BaseModel[models.PostID]{
			ID:        models.PostID(fmt.Sprintf("%025s", "Post1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Rating: models.SFW,
	}

	validPost, err = CreatePost(ctx, validPost)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// Setup Tests
	validUser01 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User1"))}}
	validUser01, err = CreateUser(ctx, validUser01)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	validUser02 := models.User{BaseModel: models.BaseModel[models.UserID]{ID: models.UserID(fmt.Sprintf("%025s", "User2"))}}
	validUser02, err = CreateUser(ctx, validUser02)

	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// -- Create PostReport to test with
	validPostReport := models.PostReport{
		BaseModel: models.BaseModel[models.PostReportID]{
			ID:        models.PostReportID(fmt.Sprintf("%025s", "PostReport1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		PostID:            validPost.ID,
		ReportBy:          validUser01.ID,
		ReportDescription: "aaa",
		AuditBy:           nil,
		AuditDescription:  "",
		ReportType:        models.Duplicate,
		ReportState:       models.Rejected,
	}
	// --

	validPostReport, err = CreatePostReport(ctx, validPostReport)
	if err != nil {
		t.Fatalf("CreatePostReport err: %v", err)
	}
	// --
	// --

	// -- -- Tests
	type args struct {
		ctx context.Context
		id  models.PostReportID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "Test 01: Delete Valid PostReport",
			args: args{
				ctx: ctx,
				id:  validPostReport.ID,
			},
			wantErr: false,
		},
		{
			name: "Test 02: Delete not existed PostReport",
			args: args{
				ctx: ctx,
				id:  validPostReport.ID,
			},
			wantErr: false,
		},
		{
			name: "Test 03: Empty PostReportID",
			args: args{
				ctx: ctx,
				id:  "",
			},
			wantErr: true,
		},
		{
			name: "Test 04: Short PostReportID",
			args: args{
				ctx: ctx,
				id:  "111",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := DeletePostReport(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr {
				t.Errorf("DeletePostReport() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func checkPostReportID(got models.PostReport, want models.PostReport) bool {
	if got.ID != want.ID {
		return false
	}

	return true
}