package projects

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"senlinai-agent/backend/internal/models"
)

type Service struct {
}

type Dashboard struct {
	ProjectID          uint  `json:"project_id"`
	PendingInboxCount  int64 `json:"pending_inbox_count"`
	OpenTaskCount      int64 `json:"open_task_count"`
	RecentNoteCount    int64 `json:"recent_note_count"`
	RecentSessionCount int64 `json:"recent_session_count"`
}

type WorkbenchProject struct {
	ID          uint   `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Initials    string `json:"initials"`
	UnreadCount int64  `json:"unreadCount"`
}

type WorkbenchChannel struct {
	ID        string `json:"id"`
	ProjectID uint   `json:"projectID"`
	Type      string `json:"type"`
	Title     string `json:"title"`
	Icon      string `json:"icon"`
	Count     *int64 `json:"count,omitempty"`
	URL       string `json:"url,omitempty"`
	SortOrder int    `json:"sortOrder"`
}

type InboxMessage struct {
	ID      string `json:"id"`
	Source  string `json:"source"`
	Title   string `json:"title"`
	Summary string `json:"summary"`
	Status  string `json:"status"`
	Tag     string `json:"tag"`
	Time    string `json:"time"`
}

type WorkTask struct {
	ID        string `json:"id"`
	Title     string `json:"title"`
	Summary   string `json:"summary"`
	Completed bool   `json:"completed"`
	Owner     string `json:"owner"`
	Due       string `json:"due"`
	Tag       string `json:"tag"`
}

type AISessionItem struct {
	ID         string   `json:"id"`
	Title      string   `json:"title"`
	Summary    string   `json:"summary"`
	UpdatedAt  string   `json:"updatedAt"`
	References []string `json:"references"`
}

type NoteSourceItem struct {
	ID        string `json:"id"`
	Kind      string `json:"kind"`
	Title     string `json:"title"`
	UpdatedAt string `json:"updatedAt"`
	Tag       string `json:"tag"`
	Source    string `json:"source"`
}

type CronPlan struct {
	ID         string `json:"id"`
	Title      string `json:"title"`
	Schedule   string `json:"schedule"`
	NextRun    string `json:"nextRun"`
	Enabled    bool   `json:"enabled"`
	LastResult string `json:"lastResult"`
	Owner      string `json:"owner"`
}

type ProjectWorkspace struct {
	Project        WorkbenchProject   `json:"project"`
	Channels       []WorkbenchChannel `json:"channels"`
	Tags           []string           `json:"tags"`
	RecentSessions []AISessionItem    `json:"recentSessions"`
	Inbox          []InboxMessage     `json:"inbox"`
	Tasks          []WorkTask         `json:"tasks"`
	AISessions     []AISessionItem    `json:"aiSessions"`
	NotesSources   []NoteSourceItem   `json:"notesSources"`
	CronPlans      []CronPlan         `json:"cronPlans"`
}

func NewService() *Service {
	return &Service{}
}

func (s *Service) CreateProject(ownerID uint, name string, description string) (*models.SenlinAgentProject, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return nil, errors.New("project name is required")
	}
	project := &models.SenlinAgentProject{OwnerID: ownerID, Name: name, Description: description}
	return project, models.DBService.Create(project).Error
}

func (s *Service) ListProjects(ownerID uint) ([]models.SenlinAgentProject, error) {
	var projects []models.SenlinAgentProject
	err := models.DBService.Where("owner_id = ?", ownerID).Order("updated_at desc").Find(&projects).Error
	return projects, err
}

func (s *Service) CreateTag(projectID uint, name string) (*models.SenlinAgentTag, error) {
	name = strings.TrimSpace(name)
	if name == "" {
		return nil, errors.New("tag name is required")
	}
	tag := &models.SenlinAgentTag{ProjectID: projectID, Name: name}
	return tag, models.DBService.Create(tag).Error
}

func (s *Service) ListTags(projectID uint) ([]models.SenlinAgentTag, error) {
	var tags []models.SenlinAgentTag
	err := models.DBService.Where("project_id = ?", projectID).Order("name asc").Find(&tags).Error
	return tags, err
}

func (s *Service) Dashboard(ownerID uint, projectID uint) (Dashboard, error) {
	var project models.SenlinAgentProject
	if err := models.DBService.Where("id = ? AND owner_id = ?", projectID, ownerID).First(&project).Error; err != nil {
		return Dashboard{}, err
	}
	dashboard := Dashboard{ProjectID: projectID}
	if err := models.DBService.Model(&models.SenlinAgentInboxItem{}).Where("project_id = ? AND status = ?", projectID, "open").Count(&dashboard.PendingInboxCount).Error; err != nil {
		return dashboard, err
	}
	if err := models.DBService.Model(&models.SenlinAgentTask{}).Where("project_id = ? AND status <> ?", projectID, "done").Count(&dashboard.OpenTaskCount).Error; err != nil {
		return dashboard, err
	}
	if err := models.DBService.Model(&models.SenlinAgentNote{}).Where("project_id = ?", projectID).Count(&dashboard.RecentNoteCount).Error; err != nil {
		return dashboard, err
	}
	if err := models.DBService.Model(&models.SenlinAgentAISession{}).Where("project_id = ?", projectID).Count(&dashboard.RecentSessionCount).Error; err != nil {
		return dashboard, err
	}
	return dashboard, nil
}

func (s *Service) Workspace(ownerID uint, projectID uint) (ProjectWorkspace, error) {
	var project models.SenlinAgentProject
	if err := models.DBService.Where("id = ? AND owner_id = ?", projectID, ownerID).First(&project).Error; err != nil {
		return ProjectWorkspace{}, err
	}

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

	return ProjectWorkspace{
		Project: WorkbenchProject{
			ID:          project.ID,
			Name:        project.Name,
			Description: project.Description,
			Initials:    projectInitials(project.Name),
			UnreadCount: counts.inbox,
		},
		Channels:       channels,
		Tags:           tags,
		RecentSessions: aiSessions,
		Inbox:          inboxItems,
		Tasks:          tasks,
		AISessions:     aiSessions,
		NotesSources:   notesSources,
		CronPlans:      cronPlans,
	}, nil
}

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

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) ([]string, error) {
	var tags []models.SenlinAgentTag
	if err := models.DBService.Where("project_id = ?", projectID).Order("name asc").Find(&tags).Error; err != nil {
		return nil, err
	}
	names := []string{"all"}
	for _, tag := range tags {
		names = append(names, tag.Name)
	}
	return names, nil
}

func (s *Service) workspaceChannels(projectID uint, counts workspaceCounts) ([]WorkbenchChannel, error) {
	total := counts.inbox + counts.tasks + counts.aiSessions + counts.notesSources + counts.cronPlans
	channels := []WorkbenchChannel{
		{ID: fmt.Sprintf("%d-overview", projectID), ProjectID: projectID, Type: "overview", Title: "Overview", Icon: "home", Count: &total, SortOrder: 1},
		{ID: fmt.Sprintf("%d-inbox", projectID), ProjectID: projectID, Type: "inbox", Title: "Message Flow", Icon: "mail", Count: &counts.inbox, SortOrder: 2},
		{ID: fmt.Sprintf("%d-tasks", projectID), ProjectID: projectID, Type: "tasks", Title: "Work Plan", Icon: "list", Count: &counts.tasks, SortOrder: 3},
		{ID: fmt.Sprintf("%d-ai", projectID), ProjectID: projectID, Type: "ai_sessions", Title: "AI Sessions", Icon: "sparkles", Count: &counts.aiSessions, SortOrder: 4},
		{ID: fmt.Sprintf("%d-notes", projectID), ProjectID: projectID, Type: "notes_sources", Title: "Notes & Sources", Icon: "file", Count: &counts.notesSources, SortOrder: 5},
		{ID: fmt.Sprintf("%d-cron", projectID), ProjectID: projectID, Type: "cron", Title: "Cron Plans", Icon: "clock", Count: &counts.cronPlans, 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, WorkbenchChannel{
			ID:        fmt.Sprintf("%d-custom-%d", projectID, channel.ID),
			ProjectID: projectID,
			Type:      "custom_link",
			Title:     channel.Title,
			Icon:      channel.Icon,
			URL:       channel.URL,
			SortOrder: channel.SortOrder,
		})
	}
	return channels, nil
}

func (s *Service) workspaceInbox(projectID uint) ([]InboxMessage, error) {
	var items []models.SenlinAgentInboxItem
	if err := models.DBService.Where("project_id = ?", projectID).Order("updated_at desc, id desc").Limit(50).Find(&items).Error; err != nil {
		return nil, err
	}
	messages := make([]InboxMessage, 0, len(items))
	for _, item := range items {
		messages = append(messages, InboxMessage{
			ID:      fmt.Sprint(item.ID),
			Source:  item.SourceType,
			Title:   item.Title,
			Summary: item.Body,
			Status:  item.Status,
			Tag:     "",
			Time:    displayTime(item.UpdatedAt),
		})
	}
	return messages, nil
}

func (s *Service) workspaceTasks(projectID uint) ([]WorkTask, 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
	}
	items := make([]WorkTask, 0, len(tasks))
	for _, task := range tasks {
		owner, err := userDisplayName(task.AssigneeID, task.CreatedBy)
		if err != nil {
			return nil, err
		}
		items = append(items, WorkTask{
			ID:        fmt.Sprint(task.ID),
			Title:     task.Title,
			Summary:   task.Description,
			Completed: task.Status == "done",
			Owner:     owner,
			Due:       displayOptionalTime(task.DueAt),
			Tag:       "",
		})
	}
	return items, nil
}

func (s *Service) workspaceAISessions(projectID uint) ([]AISessionItem, 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([]AISessionItem, 0, len(sessions))
	for _, session := range sessions {
		items = append(items, AISessionItem{
			ID:         fmt.Sprint(session.ID),
			Title:      session.Title,
			Summary:    session.Context,
			UpdatedAt:  displayTime(session.UpdatedAt),
			References: []string{},
		})
	}
	return items, nil
}

func (s *Service) workspaceNotesSources(projectID uint) ([]NoteSourceItem, 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([]NoteSourceItem, 0, len(notes))
	for _, note := range notes {
		items = append(items, NoteSourceItem{
			ID:        fmt.Sprintf("note-%d", note.ID),
			Kind:      "note",
			Title:     note.Title,
			UpdatedAt: displayTime(note.UpdatedAt),
			Tag:       "",
			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, NoteSourceItem{
			ID:        fmt.Sprintf("%s-%d", source.Kind, source.ID),
			Kind:      source.Kind,
			Title:     source.Title,
			UpdatedAt: displayTime(source.UpdatedAt),
			Tag:       "",
			Source:    sourceDisplayName(source),
		})
	}
	return items, nil
}

func (s *Service) workspaceCronPlans(projectID uint) ([]CronPlan, 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([]CronPlan, 0, len(plans))
	for _, plan := range plans {
		owner, err := userDisplayName(nil, plan.CreatedBy)
		if err != nil {
			return nil, err
		}
		items = append(items, CronPlan{
			ID:         fmt.Sprint(plan.ID),
			Title:      plan.Title,
			Schedule:   plan.Schedule,
			NextRun:    displayOptionalTime(plan.NextRunAt),
			Enabled:    plan.Enabled,
			LastResult: defaultString(plan.LastResult, "Not run yet"),
			Owner:      owner,
		})
	}
	return items, nil
}

func projectInitials(name string) string {
	words := strings.Fields(name)
	if len(words) == 0 {
		return "P"
	}
	for i := len(words) - 1; i >= 0; i-- {
		word := strings.Trim(words[i], "#_-")
		if isShortCode(word) {
			return strings.ToUpper(word)
		}
	}
	if len(words) == 1 {
		runes := []rune(words[0])
		if len(runes) == 1 {
			return strings.ToUpper(string(runes[0]))
		}
		return strings.ToUpper(string(runes[:2]))
	}
	return strings.ToUpper(string([]rune(words[0])[0]) + string([]rune(words[len(words)-1])[0]))
}

func isShortCode(value string) bool {
	runes := []rune(value)
	if len(runes) == 0 || len(runes) > 3 {
		return false
	}
	hasDigit := false
	for _, r := range runes {
		if r >= '0' && r <= '9' {
			hasDigit = true
			continue
		}
		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
			continue
		}
		return false
	}
	return hasDigit
}

func displayTime(value time.Time) string {
	if value.IsZero() {
		return ""
	}
	return value.UTC().Format("2006-01-02 15:04")
}

func displayOptionalTime(value *time.Time) string {
	if value == nil {
		return ""
	}
	return displayTime(*value)
}

func sourceDisplayName(source models.SenlinAgentSource) string {
	switch source.Kind {
	case "file":
		return "Attachment"
	case "link":
		return "URL"
	default:
		return strings.Title(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 {
		return fmt.Sprintf("User %d", userID), nil
	}
	return user.DisplayName, nil
}

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