package common

import (
	"io"
	"strings"
	"time"

	collabent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/collaboration"
	engineerent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/engineer"
	taskent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/task"
	ticketent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/ticket"
	wsent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/workspace"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/utils/printer"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/utils/printer/table"
	timeutil "github.com/tidbcloud/aisupport/packages/sdk-go/infra/utils/time"
)

const timeLayout = "2006-01-02 15:04:05 MST"

var Printer printer.Printer

func SetupPrinter(format string, output io.Writer) error {
	registerTicketManagerTableSchemes()

	var err error
	Printer, err = printer.NewPrinter(format, output)
	return err
}

func registerTicketManagerTableSchemes() {
	table.SchemeStore.Add(wsent.Workspace{}, []string{
		"ID", "Name", "Severity", "Members", "Status", "TaskListID", "StartedAt", "ArchivedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.Workspace](obj)
		return []any{value.ID, value.Name, value.Severity, workspaceMembersValue(value.Members), value.Status, value.TaskListID, tsValue(value.StartedAt), tsPtrValue(value.ArchivedAt)}
	})
	table.SchemeStore.Add(wsent.IssueBinding{}, []string{
		"ID", "WorkspaceID", "IssueKey", "BoundBy", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.IssueBinding](obj)
		return []any{value.ID, value.WorkspaceID, value.IssueKey, value.BoundBy, tsValue(value.CreatedAt)}
	})
	table.SchemeStore.Add(wsent.AlertBinding{}, []string{
		"ID", "WorkspaceID", "AlertKey", "BoundBy", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.AlertBinding](obj)
		return []any{value.ID, value.WorkspaceID, value.AlertKey, value.BoundBy, tsValue(value.CreatedAt)}
	})
	table.SchemeStore.Add(wsent.TenantBinding{}, []string{
		"ID", "WorkspaceID", "TenantID", "TenantName", "TenantRegion", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.TenantBinding](obj)
		return []any{value.ID, value.WorkspaceID, value.TenantID, value.TenantName, value.TenantRegion, tsValue(value.CreatedAt)}
	})
	table.SchemeStore.Add(wsent.ClusterBinding{}, []string{
		"ID", "WorkspaceID", "ClusterID", "ClusterName", "ClusterType", "ClusterProvider", "ClusterRegion", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.ClusterBinding](obj)
		return []any{
			value.ID,
			value.WorkspaceID,
			value.ClusterID,
			value.ClusterName,
			value.ClusterType,
			value.ClusterProvider,
			value.ClusterRegion,
			tsValue(value.CreatedAt),
		}
	})
	table.SchemeStore.Add(wsent.CollaborationResourceBinding{}, []string{
		"ID", "WorkspaceID", "ResourceID", "BoundBy", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.CollaborationResourceBinding](obj)
		return []any{value.ID, value.WorkspaceID, value.ResourceID, value.BoundBy, tsValue(value.CreatedAt)}
	})
	table.SchemeStore.Add(wsent.CollaborationResource{}, []string{
		"BindingID", "WorkspaceID", "ResourceID", "Type", "Provider", "RemoteID", "Name", "Link", "BoundBy", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.CollaborationResource](obj)
		var resource collabent.Resource
		if value.CollaborationResource != nil {
			resource = *value.CollaborationResource
		}
		return []any{
			value.ID,
			value.WorkspaceID,
			value.ResourceID,
			resource.Type,
			resource.Provider,
			resource.ResourceID,
			resource.Name,
			resource.Link,
			value.BoundBy,
			tsValue(value.CreatedAt),
		}
	})
	table.SchemeStore.Add(wsent.Advice{}, []string{
		"ID", "WorkspaceID", "Type", "Status", "AppliedAt", "CreatedAt", "UpdatedAt",
	}, func(obj any) []any {
		value := entityValue[wsent.Advice](obj)
		return []any{value.ID, value.WorkspaceID, value.Type, value.Status, tsPtrValue(value.AppliedAt), tsValue(value.CreatedAt), tsPtrValue(value.UpdatedAt)}
	})

	table.SchemeStore.Add(ticketent.Ticket{}, []string{
		"ID", "Key", "Title", "Status", "Assignee", "CreatedAt", "UpdatedAt",
	}, func(obj any) []any {
		value := entityValue[ticketent.Ticket](obj)
		return []any{value.ID, value.JiraIssueKey, value.Title, value.CurrentJiraStatus, value.JiraAssignee, timeValue(value.CreatedAt), timeValue(value.UpdatedAt)}
	})
	table.SchemeStore.Add(ticketent.JiraIssue{}, []string{
		"ID", "Key", "Summary", "Status", "Priority", "Assignee", "CreatedAt", "UpdatedAt", "ResolvedAt",
	}, func(obj any) []any {
		value := entityValue[ticketent.JiraIssue](obj)
		return []any{value.ID, value.Key, value.Summary, value.Status, value.Priority, jiraUserEmail(value.Assignee), tsValue(value.CreatedAt), tsValue(value.UpdatedAt), tsPtrValue(value.ResolvedAt)}
	})
	table.SchemeStore.Add(ticketent.TimelineEvent{}, []string{
		"ID", "TicketID", "Type", "OccurredAt", "ActorType", "ActorID", "Source",
	}, func(obj any) []any {
		value := entityValue[ticketent.TimelineEvent](obj)
		return []any{value.ID, value.TicketID, value.EventType, timeValue(value.OccurredAt), value.ActorType, value.ActorID, value.Source}
	})

	table.SchemeStore.Add(taskent.TaskList{}, []string{
		"ID", "Name", "Owner", "CreatedAt", "UpdatedAt",
	}, func(obj any) []any {
		value := entityValue[taskent.TaskList](obj)
		return []any{value.ID, value.Name, value.Owner, tsValue(value.CreatedAt), tsPtrValue(value.UpdatedAt)}
	})
	table.SchemeStore.Add(taskent.Task{}, []string{
		"ID", "Title", "Priority", "Creator", "TaskListID", "ParentTaskID", "StartedAt", "DueAt", "CompletedAt",
	}, func(obj any) []any {
		value := entityValue[taskent.Task](obj)
		return []any{value.ID, value.Title, value.Priority, value.Creator, value.TaskListID, value.ParentTaskID, tsPtrValue(value.StartedAt), tsPtrValue(value.DueAt), tsPtrValue(value.CompletedAt)}
	})

	table.SchemeStore.Add(collabent.Resource{}, []string{
		"ID", "Type", "Provider", "ResourceID", "Name", "CreatedAt",
	}, func(obj any) []any {
		value := entityValue[collabent.Resource](obj)
		return []any{value.ID, value.Type, value.Provider, value.ResourceID, value.Name, tsValue(value.CreatedAt)}
	})

	table.SchemeStore.Add(engineerent.Engineer{}, []string{
		"Email", "DispatchEnabled", "IsOnCall",
	}, func(obj any) []any {
		value := entityValue[engineerent.Engineer](obj)
		return []any{value.Email, value.DispatchState.DispatchEnabled, value.Availability.IsOnCall}
	})
}

func entityValue[T any](obj any) T {
	switch value := obj.(type) {
	case T:
		return value
	case *T:
		if value != nil {
			return *value
		}
	}
	var zero T
	return zero
}

func workspaceMembersValue(members []wsent.WorkspaceMember) string {
	values := make([]string, 0, len(members))
	for _, member := range members {
		values = append(values, member.Email+":"+string(member.Role)+":"+member.Group)
	}
	return strings.Join(values, ",")
}

func timeValue(value time.Time) string {
	return value.In(time.Local).Format(timeLayout)
}

func tsValue(value int64) any {
	return timeValue(*timeutil.TsToTime(&value))
}

func tsPtrValue(value *int64) any {
	if value == nil {
		return ""
	}
	return timeValue(*timeutil.TsToTime(value))
}

func jiraUserEmail(user *ticketent.JiraUser) string {
	if user == nil {
		return ""
	}
	return user.Email
}
