package projects

import (
	"errors"
	"strings"
	"time"

	"gorm.io/gorm"
	"senlinai-agent/backend/internal/models"
)

type workspaceCounts struct {
	inbox        int64
	tasks        int64
	aiSessions   int64
	notesSources int64
	cronPlans    int64
}

// Workspace 在确认项目归属后执行首屏聚合；后续子查询只接收已授权的内部主键。
func (s *Service) Workspace(ownerID uint, projectIdentity string) (WorkspaceDTO, error) {
	project, err := FindOwnedProject(ownerID, projectIdentity)
	if err != nil {
		return WorkspaceDTO{}, err
	}

	counts, err := s.workspaceCounts(project.ID)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	tags, err := s.workspaceTags(project.ID)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	channels, err := s.workspaceChannels(project.ID, project.Identity, counts)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	inboxItems, err := s.workspaceInbox(project.ID, project.Identity)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	tasks, err := s.workspaceTasks(project.ID, project.Identity)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	aiSessions, err := s.workspaceAISessions(project.ID, project.Identity)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	notesSources, err := s.workspaceNotesSources(project.ID, project.Identity)
	if err != nil {
		return WorkspaceDTO{}, err
	}
	cronPlans, err := s.workspaceCronPlans(project.ID, project.Identity)
	if err != nil {
		return WorkspaceDTO{}, err
	}

	return WorkspaceDTO{
		Project: WorkspaceProjectDTO{
			ID:          project.Identity,
			Name:        project.Name,
			Identifier:  project.Identifier,
			Icon:        project.Icon,
			Background:  project.Background,
			Description: project.Description,
			Initials:    defaultString(project.Identifier, projectInitials(project.Name)),
			UnreadCount: counts.inbox,
		},
		Channels:       channels,
		Tags:           tags,
		RecentSessions: aiSessions,
		Inbox:          inboxItems,
		Tasks:          tasks,
		AISessions:     aiSessions,
		NotesSources:   notesSources,
		CronPlans:      cronPlans,
	}, nil
}

func (s *Service) workspaceCounts(projectID uint) (workspaceCounts, error) {
	var counts workspaceCounts
	if err := models.DBService.Model(&models.SenlinAgentInboxItem{}).Where("project_id = ? AND status = ?", projectID, "open").Count(&counts.inbox).Error; err != nil {
		return counts, err
	}
	if err := models.DBService.Model(&models.SenlinAgentTask{}).Where("project_id = ? AND status <> ?", projectID, "done").Count(&counts.tasks).Error; err != nil {
		return counts, err
	}
	if err := models.DBService.Model(&models.SenlinAgentAISession{}).Where("project_id = ?", projectID).Count(&counts.aiSessions).Error; err != nil {
		return counts, err
	}
	var noteCount int64
	if err := models.DBService.Model(&models.SenlinAgentNote{}).Where("project_id = ?", projectID).Count(&noteCount).Error; err != nil {
		return counts, err
	}
	var sourceCount int64
	if err := models.DBService.Model(&models.SenlinAgentSource{}).Where("project_id = ?", projectID).Count(&sourceCount).Error; err != nil {
		return counts, err
	}
	counts.notesSources = noteCount + sourceCount
	if err := models.DBService.Model(&models.SenlinAgentCronPlan{}).Where("project_id = ?", projectID).Count(&counts.cronPlans).Error; err != nil {
		return counts, err
	}
	return counts, nil
}

func (s *Service) workspaceTags(projectID uint) ([]WorkspaceTagDTO, error) {
	var tags []models.SenlinAgentTag
	if err := models.DBService.Where("project_id = ?", projectID).Order("name asc").Find(&tags).Error; err != nil {
		return nil, err
	}
	items := make([]WorkspaceTagDTO, 0, len(tags))
	for _, tag := range tags {
		items = append(items, WorkspaceTagDTO{ID: tag.Identity, Name: tag.Name})
	}
	return items, nil
}

func (s *Service) workspaceChannels(projectID uint, projectIdentity string, counts workspaceCounts) ([]WorkspaceChannelDTO, error) {
	total := counts.inbox + counts.tasks + counts.aiSessions + counts.notesSources + counts.cronPlans
	channels := []WorkspaceChannelDTO{
		{ID: projectIdentity + ":overview", ProjectID: projectIdentity, Type: "overview", Title: "Overview", Icon: "home", Count: total, URL: "", SortOrder: 1},
		{ID: projectIdentity + ":inbox", ProjectID: projectIdentity, Type: "inbox", Title: "Message Flow", Icon: "mail", Count: counts.inbox, URL: "", SortOrder: 2},
		{ID: projectIdentity + ":tasks", ProjectID: projectIdentity, Type: "tasks", Title: "Work Plan", Icon: "list", Count: counts.tasks, URL: "", SortOrder: 3},
		{ID: projectIdentity + ":ai", ProjectID: projectIdentity, Type: "ai_sessions", Title: "AI Sessions", Icon: "sparkles", Count: counts.aiSessions, URL: "", SortOrder: 4},
		{ID: projectIdentity + ":notes", ProjectID: projectIdentity, Type: "notes_sources", Title: "Notes & Sources", Icon: "file", Count: counts.notesSources, URL: "", SortOrder: 5},
		{ID: projectIdentity + ":cron", ProjectID: projectIdentity, Type: "cron", Title: "Cron Plans", Icon: "clock", Count: counts.cronPlans, URL: "", SortOrder: 6},
	}
	var customChannels []models.SenlinAgentProjectChannel
	if err := models.DBService.Where("project_id = ?", projectID).Order("sort_order asc, id asc").Find(&customChannels).Error; err != nil {
		return nil, err
	}
	for _, channel := range customChannels {
		channels = append(channels, WorkspaceChannelDTO{
			ID: channel.Identity, ProjectID: projectIdentity, Type: "custom_link", Title: channel.Title,
			Icon: channel.Icon, Count: 0, URL: channel.URL, SortOrder: channel.SortOrder,
		})
	}
	return channels, nil
}

func (s *Service) workspaceInbox(projectID uint, projectIdentity string) ([]WorkspaceInboxDTO, error) {
	var records []models.SenlinAgentInboxItem
	if err := models.DBService.Where("project_id = ?", projectID).Order("updated_at desc, id desc").Limit(50).Find(&records).Error; err != nil {
		return nil, err
	}
	items := make([]WorkspaceInboxDTO, 0, len(records))
	for _, record := range records {
		items = append(items, WorkspaceInboxDTO{
			ID: record.Identity, ProjectID: projectIdentity, Source: record.SourceType, Title: record.Title,
			Summary: record.Body, Status: record.Status, Time: record.UpdatedAt.UTC(),
		})
	}
	return items, nil
}

func (s *Service) workspaceTasks(projectID uint, projectIdentity string) ([]WorkspaceTaskDTO, error) {
	var tasks []models.SenlinAgentTask
	if err := models.DBService.Where("project_id = ?", projectID).Order("CASE WHEN status = 'done' THEN 1 ELSE 0 END asc").Order("sort_order asc, updated_at desc, id desc").Limit(50).Find(&tasks).Error; err != nil {
		return nil, err
	}
	tags, err := workspaceTaskTags(projectID, tasks)
	if err != nil {
		return nil, err
	}
	items := make([]WorkspaceTaskDTO, 0, len(tasks))
	for _, task := range tasks {
		owner, err := userDisplayName(task.AssigneeID, task.CreatedBy)
		if err != nil {
			return nil, err
		}
		// 当前模型没有真实完成时间字段，不能用更新时间伪装；CompletedAt 保持 nil。
		item := WorkspaceTaskDTO{
			ID: task.Identity, ProjectID: projectIdentity, Title: task.Title, Summary: task.Description,
			Completed: task.Status == "done", Owner: owner, Due: utcOptionalTime(task.DueAt), CreatedAt: task.CreatedAt.UTC(),
		}
		if tag, ok := tags[task.ID]; ok {
			item.TagID = &tag.ID
			item.Tag = tag.Name
		}
		items = append(items, item)
	}
	return items, nil
}

// workspaceTaskTags 同时限定 tag 主键和项目主键，避免损坏数据把其他项目标签带入任务响应。
func workspaceTaskTags(projectID uint, tasks []models.SenlinAgentTask) (map[uint]WorkspaceTagDTO, error) {
	tagIDs := make([]uint, 0, len(tasks))
	for _, task := range tasks {
		if task.TagID != nil {
			tagIDs = append(tagIDs, *task.TagID)
		}
	}
	result := make(map[uint]WorkspaceTagDTO)
	if len(tagIDs) == 0 {
		return result, nil
	}
	var tags []models.SenlinAgentTag
	if err := models.DBService.Where("project_id = ? AND id IN ?", projectID, tagIDs).Find(&tags).Error; err != nil {
		return nil, err
	}
	byID := make(map[uint]WorkspaceTagDTO, len(tags))
	for _, tag := range tags {
		byID[tag.ID] = WorkspaceTagDTO{ID: tag.Identity, Name: tag.Name}
	}
	for _, task := range tasks {
		if task.TagID != nil {
			if tag, ok := byID[*task.TagID]; ok {
				result[task.ID] = tag
			}
		}
	}
	return result, nil
}

func (s *Service) workspaceAISessions(projectID uint, projectIdentity string) ([]WorkspaceAISessionDTO, error) {
	var sessions []models.SenlinAgentAISession
	if err := models.DBService.Where("project_id = ?", projectID).Order("updated_at desc, id desc").Limit(50).Find(&sessions).Error; err != nil {
		return nil, err
	}
	items := make([]WorkspaceAISessionDTO, 0, len(sessions))
	for _, session := range sessions {
		items = append(items, WorkspaceAISessionDTO{
			ID: session.Identity, ProjectID: projectIdentity, Title: session.Title, Summary: session.Context,
			UpdatedAt: session.UpdatedAt.UTC(), References: []string{},
		})
	}
	return items, nil
}

func (s *Service) workspaceNotesSources(projectID uint, projectIdentity string) ([]WorkspaceNoteSourceDTO, error) {
	var notes []models.SenlinAgentNote
	if err := models.DBService.Where("project_id = ?", projectID).Order("updated_at desc, id desc").Limit(50).Find(&notes).Error; err != nil {
		return nil, err
	}
	items := make([]WorkspaceNoteSourceDTO, 0, len(notes))
	for _, note := range notes {
		items = append(items, WorkspaceNoteSourceDTO{
			ID: note.Identity, ProjectID: projectIdentity, Kind: "note", Title: note.Title,
			UpdatedAt: note.UpdatedAt.UTC(), Source: "Markdown",
		})
	}

	var sources []models.SenlinAgentSource
	if err := models.DBService.Where("project_id = ?", projectID).Order("updated_at desc, id desc").Limit(50).Find(&sources).Error; err != nil {
		return nil, err
	}
	for _, source := range sources {
		items = append(items, WorkspaceNoteSourceDTO{
			ID: source.Identity, ProjectID: projectIdentity, Kind: source.Kind, Title: source.Title,
			UpdatedAt: source.UpdatedAt.UTC(), Source: sourceDisplayName(source),
		})
	}
	return items, nil
}

func (s *Service) workspaceCronPlans(projectID uint, projectIdentity string) ([]WorkspaceCronPlanDTO, error) {
	var plans []models.SenlinAgentCronPlan
	if err := models.DBService.Where("project_id = ?", projectID).Order("enabled desc, updated_at desc, id desc").Limit(50).Find(&plans).Error; err != nil {
		return nil, err
	}
	items := make([]WorkspaceCronPlanDTO, 0, len(plans))
	for _, plan := range plans {
		owner, err := userDisplayName(nil, plan.CreatedBy)
		if err != nil {
			return nil, err
		}
		items = append(items, WorkspaceCronPlanDTO{
			ID: plan.Identity, ProjectID: projectIdentity, Title: plan.Title, Schedule: plan.Schedule,
			NextRun: utcOptionalTime(plan.NextRunAt), Enabled: plan.Enabled,
			LastResult: strings.TrimSpace(plan.LastResult), Owner: owner,
		})
	}
	return items, nil
}

func utcOptionalTime(value *time.Time) *time.Time {
	if value == nil {
		return nil
	}
	utc := value.UTC()
	return &utc
}

func sourceDisplayName(source models.SenlinAgentSource) string {
	switch source.Kind {
	case "file":
		return "Attachment"
	case "link":
		return "URL"
	default:
		return source.Kind
	}
}

func userDisplayName(assigneeID *uint, createdBy uint) (string, error) {
	userID := createdBy
	if assigneeID != nil && *assigneeID != 0 {
		userID = *assigneeID
	}
	var user models.SenlinAgentUser
	if err := models.DBService.Select("display_name").Where("id = ?", userID).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return "未知用户", nil
		}
		return "", err
	}
	return user.DisplayName, nil
}

func defaultString(value string, fallback string) string {
	if strings.TrimSpace(value) == "" {
		return fallback
	}
	return value
}
