package issueloader

import (
	"context"
	"testing"
	"time"

	gojira "github.com/andygrunwald/go-jira"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	jiraent "github.com/tidbcloud/devops/entity/jira"
	"github.com/tidbcloud/devops/pkg/infra/clients/jiracli"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/infra/utils/test"
)

func TestIssueLoader(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "IssueLoader Suite", test.ReporterConfig())
}

var _ = Describe("[IssueLoader]", func() {
	var fw = test.Framework()
	var ctx context.Context
	var loader IssueLoader
	var jira *jiracli.MockJiraClient
	now := time.Now()

	BeforeEach(func() {
		ctx = log.NewContext(context.Background(), fw.Logger)
		jira = jiracli.NewMockJiraClient(fw.Ctrl)
		loader = NewIssueLoader(jira)
	})

	Describe("[Build]", func() {
		var rawIssue gojira.Issue

		BeforeEach(func() {
			eta := time.Date(2026, 6, 5, 0, 0, 0, 0, time.UTC)
			rawIssue = gojira.Issue{
				Key: "DM-1234",
				Fields: &gojira.IssueFields{
					Summary:     "test issue",
					Description: "this is a test issue",
					Created:     gojira.Time(now),
					Updated:     gojira.Time(now),
					Reporter: &gojira.User{
						AccountID:   "reporter-001",
						DisplayName: "Reporter User",
					},
					Assignee: &gojira.User{
						AccountID:   "assignee-001",
						DisplayName: "Assignee User",
					},
					Status: &gojira.Status{
						Name: "In Progress",
					},
					Priority: &gojira.Priority{
						Name: "High",
					},
					Comments: &gojira.Comments{
						Comments: []*gojira.Comment{
							{
								ID:      "1001",
								Created: now.Format(JiraISOTimeLayout),
								Updated: now.Format(JiraISOTimeLayout),
								Author: gojira.User{
									DisplayName:  "Commenter1",
									EmailAddress: "test@pingcap.com",
								},
							},
						},
					},
					IssueLinks: []*gojira.IssueLink{
						{
							ID: "2001",
							Type: gojira.IssueLinkType{
								Name: "Relates",
							},
							InwardIssue: &gojira.Issue{
								Key: "DM-5678",
							},
						},
						{
							ID: "2002",
							Type: gojira.IssueLinkType{
								Name: "Relates",
							},
							OutwardIssue: &gojira.Issue{
								Key: "DM-5678",
							},
						},
					},
					Resolutiondate: gojira.Time(now),
					Unknowns: map[string]any{
						TCOCP_ETA_FIELD: eta.Format("2006-01-02"),
					},
				},
			}
		})

		Describe("[IssueInJira]", func() {
			var expIssue *jiraent.IssueInJira

			BeforeEach(func() {
				eta := time.Date(2026, 6, 5, 0, 0, 0, 0, time.UTC)
				expIssue = &jiraent.IssueInJira{
					ID:         rawIssue.ID,
					Key:        rawIssue.Key,
					Summary:    rawIssue.Fields.Summary,
					Status:     rawIssue.Fields.Status.Name,
					CreatedAt:  now.Unix(),
					UpdatedAt:  now.Unix(),
					ResolvedAt: pointer.Pointer(now.Unix()),
					ETA:        pointer.Pointer(eta.Unix()),

					Description: rawIssue.Fields.Description,
					Priority:    rawIssue.Fields.Priority.Name,
					Project: jiraent.Project{
						Key: rawIssue.Fields.Project.Key,
					},
					Assignee: &jiraent.User{
						Email:       rawIssue.Fields.Assignee.EmailAddress,
						DisplayName: rawIssue.Fields.Assignee.DisplayName,
					},
					Reporter: &jiraent.User{
						Email:       rawIssue.Fields.Reporter.EmailAddress,
						DisplayName: rawIssue.Fields.Reporter.DisplayName,
					},
					IssueLinks: []jiraent.IssueLink{
						{
							TypeName:       "Relates",
							InwardIssueKey: pointer.Pointer("DM-5678"),
						},
						{
							TypeName:        "Relates",
							OutwardIssueKey: pointer.Pointer("DM-5678"),
						},
					},
					Comments: []jiraent.Comment{
						{
							ID:        "1001",
							CreatedAt: now.Unix(),
							UpdatedAt: now.Unix(),
							Author: &jiraent.User{
								Email:       "test@pingcap.com",
								DisplayName: "Commenter1",
							},
						},
					},
				}
			})

			It("should build from raw issue", func() {
				issue, err := loader.(*issueLoader).buildIssueInJiraFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for ST/TCIC", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "ST-1234"
				rawIssue.Fields.Project.Key = "ST"
				rawIssue.Fields.Unknowns[JSM_TENANT_ID_FIELD] = []any{
					map[string]any{
						"workspaceId": "workspace-001",
						"objectId":    "object-001",
					},
				}
				rawIssue.Fields.Unknowns[JSM_CLUSTER_ID_FIELD] = []any{
					map[string]any{
						"workspaceId": "workspace-002",
						"objectId":    "object-002",
					},
				}
				expIssue.Key = rawIssue.Key
				expIssue.Project.Key = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				jira.EXPECT().GetAssetObject(ctx, "workspace-001", "object-001").Return(&jiracli.AssetObject{
					Label: tenantID,
				}, nil).Times(1)
				jira.EXPECT().GetAssetObject(ctx, "workspace-002", "object-002").Return(&jiracli.AssetObject{
					Label: clusterID,
				}, nil).Times(1)

				issue, err := loader.(*issueLoader).buildIssueInJiraFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for TCOC", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "TCOC-1234"
				rawIssue.Fields.Project.Key = "TCOC"
				rawIssue.Fields.Unknowns[TENANT_ID_FIELD] = tenantID
				rawIssue.Fields.Unknowns[CLUSTER_ID_FIELD] = clusterID
				expIssue.Key = rawIssue.Key
				expIssue.Project.Key = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInJiraFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for JPID/APID", func() {
				tenantID := "acct-001"
				rawIssue.Key = "JPID-1234"
				rawIssue.Fields.Project.Key = "JPID"
				rawIssue.Fields.Unknowns[OP_ACCOUNT_ID_FIELD] = tenantID
				expIssue.Key = rawIssue.Key
				expIssue.Project.Key = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentOnPremise,
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInJiraFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for O11Y", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "O11Y-1234"
				rawIssue.Fields.Project.Key = "O11Y"
				rawIssue.Fields.Unknowns[TENANT_ID_FIELD] = tenantID
				rawIssue.Fields.Unknowns[CLUSTER_ID_FIELD] = clusterID
				expIssue.Key = rawIssue.Key
				expIssue.Project.Key = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInJiraFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})
		})

		Describe("[IssueSnapshot]", func() {
			var expIssue *jiraent.IssueSnapshot

			BeforeEach(func() {
				expIssue = &jiraent.IssueSnapshot{
					ID:            rawIssue.ID,
					Key:           rawIssue.Key,
					Summary:       rawIssue.Fields.Summary,
					Status:        rawIssue.Fields.Status.Name,
					CreatedAt:     now.Unix(),
					ResolvedAt:    pointer.Pointer(now.Unix()),
					Priority:      rawIssue.Fields.Priority.Name,
					ProjectKey:    rawIssue.Fields.Project.Key,
					AssigneeEmail: pointer.Pointer(rawIssue.Fields.Assignee.EmailAddress),
					ReporterEmail: pointer.Pointer(rawIssue.Fields.Reporter.EmailAddress),
				}
			})

			It("should build from raw issue", func() {
				issue, err := loader.(*issueLoader).buildIssueInRepoFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for ST/TCIC", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "ST-1234"
				rawIssue.Fields.Project.Key = "ST"
				rawIssue.Fields.Unknowns[JSM_TENANT_ID_FIELD] = []any{
					map[string]any{
						"workspaceId": "workspace-001",
						"objectId":    "object-001",
					},
				}
				rawIssue.Fields.Unknowns[JSM_CLUSTER_ID_FIELD] = []any{
					map[string]any{
						"workspaceId": "workspace-002",
						"objectId":    "object-002",
					},
				}
				expIssue.Key = rawIssue.Key
				expIssue.ProjectKey = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				jira.EXPECT().GetAssetObject(ctx, "workspace-001", "object-001").Return(&jiracli.AssetObject{
					Label: tenantID,
				}, nil).Times(1)
				jira.EXPECT().GetAssetObject(ctx, "workspace-002", "object-002").Return(&jiracli.AssetObject{
					Label: clusterID,
				}, nil).Times(1)

				issue, err := loader.(*issueLoader).buildIssueInRepoFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for TCOC", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "TCOC-1234"
				rawIssue.Fields.Project.Key = "TCOC"
				rawIssue.Fields.Unknowns[TENANT_ID_FIELD] = tenantID
				rawIssue.Fields.Unknowns[CLUSTER_ID_FIELD] = clusterID
				expIssue.Key = rawIssue.Key
				expIssue.ProjectKey = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInRepoFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for JPID/APID", func() {
				tenantID := "acct-001"
				rawIssue.Key = "JPID-1234"
				rawIssue.Fields.Project.Key = "JPID"
				rawIssue.Fields.Unknowns[OP_ACCOUNT_ID_FIELD] = tenantID
				expIssue.Key = rawIssue.Key
				expIssue.ProjectKey = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentOnPremise,
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInRepoFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})

			It("should build from raw issue for O11Y", func() {
				clusterID := "cluster-001"
				tenantID := "tenant-001"
				rawIssue.Key = "O11Y-1234"
				rawIssue.Fields.Project.Key = "O11Y"
				rawIssue.Fields.Unknowns[TENANT_ID_FIELD] = tenantID
				rawIssue.Fields.Unknowns[CLUSTER_ID_FIELD] = clusterID
				expIssue.Key = rawIssue.Key
				expIssue.ProjectKey = rawIssue.Fields.Project.Key
				expIssue.CustomerInfo = &jiraent.IssueCustomerInfo{
					IssueKey:    rawIssue.Key,
					Environment: jiraent.IssueCustomerEnvironmentCloud,
					ClusterID:   pointer.Pointer(clusterID),
					TenantID:    pointer.Pointer(tenantID),
				}

				issue, err := loader.(*issueLoader).buildIssueInRepoFromRaw(ctx, rawIssue)
				Expect(err).To(BeNil())

				err = test.IsEqual(expIssue, issue)
				Expect(err).To(BeNil())
			})
		})
	})
})
