package projects

import (
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"senlinai-agent/backend/internal/models"
)

func TestWorkspaceUsesIdentitiesUTCTimesAndProjectScopedTags(t *testing.T) {
	database := newTestDB(t)
	service := NewService()
	require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "david@example.com", DisplayName: "David", PasswordHash: "hash"}).Error)
	project, err := service.CreateProjectWithInput(1, CreateProjectRequest{
		Name: "Project A1", Identifier: "A1", Icon: "folder", Background: "#165DFF", Description: "Client strategy workspace",
	})
	require.NoError(t, err)
	other, err := service.CreateProject(1, "Project A2", "")
	require.NoError(t, err)

	projectTag := models.SenlinAgentTag{ProjectID: project.ID, Name: "UI"}
	otherTag := models.SenlinAgentTag{ProjectID: other.ID, Name: "UI"}
	require.NoError(t, database.Create(&projectTag).Error)
	require.NoError(t, database.Create(&otherTag).Error)

	localZone := time.FixedZone("UTC+8", 8*60*60)
	createdAt := time.Date(2026, 7, 20, 10, 30, 0, 0, localZone)
	updatedAt := createdAt.Add(90 * time.Minute)
	nextRun := time.Date(2026, 7, 21, 9, 0, 0, 0, localZone)
	inbox := models.SenlinAgentInboxItem{ProjectID: project.ID, CreatedBy: 1, SourceType: "Manual", Title: "Collect notes", Body: "Turn research into tasks.", Status: "open", CreatedAt: createdAt, UpdatedAt: updatedAt}
	task := models.SenlinAgentTask{ProjectID: project.ID, CreatedBy: 1, TagID: &projectTag.ID, Title: "Confirm IA", Description: "Review channel layout.", Status: "done", DueAt: &nextRun, CreatedAt: createdAt, UpdatedAt: updatedAt}
	session := models.SenlinAgentAISession{ProjectID: project.ID, CreatedBy: 1, Title: "UI critique", Context: "Discuss channel behavior.", CreatedAt: createdAt, UpdatedAt: updatedAt}
	note := models.SenlinAgentNote{ProjectID: project.ID, CreatedBy: 1, Title: "Workbench principles", Markdown: "# Notes", CreatedAt: createdAt, UpdatedAt: updatedAt}
	source := models.SenlinAgentSource{ProjectID: project.ID, CreatedBy: 1, Kind: "link", Title: "Reference board", URL: "https://example.com", CreatedAt: createdAt, UpdatedAt: updatedAt}
	channel := models.SenlinAgentProjectChannel{ProjectID: project.ID, Title: "Roadmap Board", Icon: "link", URL: "https://example.com/roadmap", SortOrder: 7, CreatedAt: createdAt, UpdatedAt: updatedAt}
	plan := models.SenlinAgentCronPlan{ProjectID: project.ID, CreatedBy: 1, Title: "Weekly review", Schedule: "0 9 * * 1", NextRunAt: &nextRun, Enabled: true, CreatedAt: createdAt, UpdatedAt: updatedAt}
	for _, record := range []any{&inbox, &task, &session, &note, &source, &channel, &plan} {
		require.NoError(t, database.Create(record).Error)
	}

	workspace, err := service.Workspace(1, project.Identity)

	require.NoError(t, err)
	require.Equal(t, project.Identity, workspace.Project.ID)
	require.Equal(t, []WorkspaceTagDTO{{ID: projectTag.Identity, Name: "UI"}}, workspace.Tags)
	require.Len(t, workspace.Channels, 7)
	require.Equal(t, project.Identity, workspace.Channels[0].ProjectID)
	require.Equal(t, channel.Identity, workspace.Channels[6].ID)
	require.Equal(t, project.Identity, workspace.Channels[6].ProjectID)

	require.Len(t, workspace.Inbox, 1)
	require.Equal(t, inbox.Identity, workspace.Inbox[0].ID)
	require.Equal(t, project.Identity, workspace.Inbox[0].ProjectID)
	require.Equal(t, updatedAt.UTC(), workspace.Inbox[0].Time)

	require.Len(t, workspace.Tasks, 1)
	require.Equal(t, task.Identity, workspace.Tasks[0].ID)
	require.Equal(t, project.Identity, workspace.Tasks[0].ProjectID)
	require.Equal(t, projectTag.Identity, *workspace.Tasks[0].TagID)
	require.Equal(t, "UI", workspace.Tasks[0].Tag)
	require.Equal(t, nextRun.UTC(), *workspace.Tasks[0].Due)
	require.Equal(t, createdAt.UTC(), workspace.Tasks[0].CreatedAt)
	require.Nil(t, workspace.Tasks[0].CompletedAt)

	require.Len(t, workspace.AISessions, 1)
	require.Equal(t, session.Identity, workspace.AISessions[0].ID)
	require.Equal(t, project.Identity, workspace.AISessions[0].ProjectID)
	require.Equal(t, updatedAt.UTC(), workspace.AISessions[0].UpdatedAt)
	require.Equal(t, session.Identity, workspace.RecentSessions[0].ID)

	require.Len(t, workspace.NotesSources, 2)
	require.Equal(t, note.Identity, workspace.NotesSources[0].ID)
	require.Equal(t, source.Identity, workspace.NotesSources[1].ID)
	for _, item := range workspace.NotesSources {
		require.Equal(t, project.Identity, item.ProjectID)
		require.Equal(t, updatedAt.UTC(), item.UpdatedAt)
	}

	require.Len(t, workspace.CronPlans, 1)
	require.Equal(t, plan.Identity, workspace.CronPlans[0].ID)
	require.Equal(t, project.Identity, workspace.CronPlans[0].ProjectID)
	require.Equal(t, nextRun.UTC(), *workspace.CronPlans[0].NextRun)

	payload, err := json.Marshal(workspace)
	require.NoError(t, err)
	require.Contains(t, string(payload), `"createdAt":"2026-07-20T02:30:00Z"`)
	require.Contains(t, string(payload), `"updatedAt":"2026-07-20T04:00:00Z"`)
	require.Contains(t, string(payload), `"nextRun":"2026-07-21T01:00:00Z"`)
	require.NotContains(t, string(payload), fmt.Sprintf(`"id":%d`, task.ID))
	require.NotContains(t, string(payload), "duration")
}

func TestWorkspaceChannelDTOSerializesRequiredCountAndURL(t *testing.T) {
	payload, err := json.Marshal(WorkspaceChannelDTO{})
	require.NoError(t, err)
	require.Contains(t, string(payload), `"count":0`)
	require.Contains(t, string(payload), `"url":""`)
}

func TestWorkspaceDoneTaskDoesNotInventCompletedAt(t *testing.T) {
	database := newTestDB(t)
	service := NewService()
	require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "owner@example.com", DisplayName: "Owner", PasswordHash: "hash"}).Error)
	project, err := service.CreateProject(1, "Alpha", "")
	require.NoError(t, err)
	createdAt := time.Date(2026, 7, 20, 1, 0, 0, 0, time.UTC)
	updatedAt := createdAt.Add(2 * time.Hour)
	require.NoError(t, database.Create(&models.SenlinAgentTask{
		ProjectID: project.ID, CreatedBy: 1, Title: "Done", Status: "done", CreatedAt: createdAt, UpdatedAt: updatedAt,
	}).Error)

	workspace, err := service.Workspace(1, project.Identity)

	require.NoError(t, err)
	require.Len(t, workspace.Tasks, 1)
	require.Nil(t, workspace.Tasks[0].CompletedAt)
	payload, err := json.Marshal(workspace.Tasks[0])
	require.NoError(t, err)
	require.Contains(t, string(payload), `"completedAt":null`)
}

func TestWorkspaceRejectsProjectOwnedByAnotherUserByIdentity(t *testing.T) {
	newTestDB(t)
	service := NewService()
	project, err := service.CreateProject(2, "Beta", "")
	require.NoError(t, err)

	_, err = service.Workspace(1, project.Identity)

	require.Error(t, err)
}
