package manage

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

type recordingScheduleMessageValidator struct {
	calls []recordedScheduleMessageValidation
}

type recordedScheduleMessageValidation struct {
	userID      string
	workspaceID string
	targetRef   string
	content     string
}

func (validator *recordingScheduleMessageValidator) ValidateMessageForUser(_ context.Context, user model.User, workspaceID string, targetRef string, content string) error {
	validator.calls = append(validator.calls, recordedScheduleMessageValidation{
		userID:      user.ID,
		workspaceID: workspaceID,
		targetRef:   targetRef,
		content:     content,
	})
	return nil
}

func TestCreateScheduleForOwnerAcceptsTaskTarget(t *testing.T) {
	t.Parallel()

	ctx, db, _ := newTriggerTestStore(t)
	now := time.Date(2026, 7, 27, 4, 0, 0, 0, time.UTC)
	fixture := seedOwnedChannelFixture(t, db, now, "schedule_task_target")
	taskScopeID := "tsk_schedule_task_target"
	mustExec(t, db, `
INSERT INTO scopes (id, workspace_id, type, identity_key, status, created_by_member_id, created_at, updated_at)
VALUES ($1, $2, 'task', $3, 'active', $4, $5, $5)
`, taskScopeID, fixture.WorkspaceID, "scope/task/"+taskScopeID, fixture.MemberID, now)

	validator := &recordingScheduleMessageValidator{}
	service := New(nil, db, nil, ServiceOptions{
		Clock:     func() time.Time { return now },
		Validator: validator,
	})
	scheduledAt := now.Add(10 * time.Minute)

	created, err := service.CreateScheduleForOwner(ctx, fixture.User.ID, fixture.WorkspaceID, UpsertInput{
		Name: "Follow up on task",
		Source: SourceInput{
			SourceKind:  SourceKindTime,
			TimeMode:    TimeModeOneShotAbsolute,
			ScheduledAt: &scheduledAt,
		},
		Target: TargetInput{
			Kind: TargetKindTask,
			Ref:  taskScopeID,
		},
		MessageTemplate: "Post the scheduled task update.",
	})
	require.NoError(t, err)
	require.Equal(t, TargetView{
		Kind:  TargetKindTask,
		Ref:   taskScopeID,
		Label: "#" + taskScopeID,
	}, created.Target)
	require.Equal(t, []recordedScheduleMessageValidation{{
		userID:      fixture.User.ID,
		workspaceID: fixture.WorkspaceID,
		targetRef:   taskScopeID,
		content:     "Post the scheduled task update.",
	}}, validator.calls)

	var storedKind TargetKind
	var storedRef string
	require.NoError(t, db.QueryRow(ctx, `
SELECT target_kind, target_ref
FROM schedule_definitions
WHERE id = $1
`, created.ID).Scan(&storedKind, &storedRef))
	require.Equal(t, TargetKindTask, storedKind)
	require.Equal(t, taskScopeID, storedRef)
}

func TestCreateScheduleForOwnerValidatesTaskThroughProductionAppPort(t *testing.T) {
	t.Parallel()

	ctx, db, dataStore := newTriggerTestStore(t)
	now := time.Date(2026, 7, 27, 4, 30, 0, 0, time.UTC)
	fixture := seedOwnedChannelFixture(t, db, now, "schedule_task_app_port")
	createdTasks, err := taskboard.New(db).CreateTasks(ctx, taskboard.CreateTasksRequest{
		Context: taskboard.NewUserServiceContext(fixture.WorkspaceID, fixture.User.ID, fixture.User.DisplayName),
		Board:   taskboard.BoardRef{ChannelID: fixture.ChannelID},
		Titles:  []string{"Schedule target validated by App"},
	})
	require.NoError(t, err)
	require.Len(t, createdTasks.Tasks, 1)
	task := createdTasks.Tasks[0]

	service := New(nil, db, dataStore.App(), ServiceOptions{Clock: func() time.Time { return now }})
	scheduledAt := now.Add(10 * time.Minute)
	created, err := service.CreateScheduleForOwner(ctx, fixture.User.ID, fixture.WorkspaceID, UpsertInput{
		Name: "Production-wired Task reminder",
		Source: SourceInput{
			SourceKind:  SourceKindTime,
			TimeMode:    TimeModeOneShotAbsolute,
			ScheduledAt: &scheduledAt,
		},
		Target:          TargetInput{Kind: TargetKindTask, Ref: task.TaskScopeID},
		MessageTemplate: "Validate without posting the Task message.",
	})
	require.NoError(t, err)
	require.Equal(t, TargetKindTask, created.Target.Kind)
	require.Equal(t, task.TaskScopeID, created.Target.Ref)

	var messageCount int64
	require.NoError(t, db.QueryRow(ctx, `SELECT COUNT(*) FROM scope_messages WHERE scope_id=$1`, task.TaskScopeID).Scan(&messageCount))
	require.Zero(t, messageCount, "schedule validation must roll back its Task discussion write")
	var discussionCount int64
	require.NoError(t, db.QueryRow(ctx, `SELECT discussion_count FROM tasks WHERE id=$1`, task.ID).Scan(&discussionCount))
	require.Zero(t, discussionCount, "schedule validation must not touch Task activity")
}

func TestScheduleTargetKindInferenceRecognizesOnlyActiveTaskScopes(t *testing.T) {
	t.Parallel()

	ctx, db, _ := newTriggerTestStore(t)
	now := time.Date(2026, 7, 27, 5, 0, 0, 0, time.UTC)
	fixture := seedOwnedChannelFixture(t, db, now, "schedule_task_target_infer")
	activeTaskScopeID := "tsk_schedule_task_target_infer_active"
	deletedTaskScopeID := "tsk_schedule_task_target_infer_deleted"
	mustExec(t, db, `
INSERT INTO scopes (id, workspace_id, type, identity_key, status, created_by_member_id, created_at, updated_at)
VALUES
  ($1, $3, 'task', $4, 'active', $6, $7, $7),
  ($2, $3, 'task', $5, 'deleted', $6, $7, $7)
`, activeTaskScopeID, deletedTaskScopeID, fixture.WorkspaceID, "scope/task/"+activeTaskScopeID, "scope/task/"+deletedTaskScopeID, fixture.MemberID, now)

	service := New(nil, db, nil, ServiceOptions{
		Clock:     func() time.Time { return now },
		Validator: &recordingScheduleMessageValidator{},
	})
	scheduledAt := now.Add(10 * time.Minute)
	input := UpsertInput{
		Name: "Inferred task target",
		Source: SourceInput{
			SourceKind:  SourceKindTime,
			TimeMode:    TimeModeOneShotAbsolute,
			ScheduledAt: &scheduledAt,
		},
		Target:          TargetInput{Ref: activeTaskScopeID},
		MessageTemplate: "Post an inferred task update.",
	}

	validated, err := service.ValidateForOwner(ctx, fixture.User.ID, fixture.WorkspaceID, input, "")
	require.NoError(t, err)
	require.Equal(t, TargetKindTask, validated.Proposed.Target.Kind)
	require.Equal(t, activeTaskScopeID, validated.Proposed.Target.Ref)

	input.Target.Ref = deletedTaskScopeID
	_, err = service.ValidateForOwner(ctx, fixture.User.ID, fixture.WorkspaceID, input, "")
	require.ErrorIs(t, err, storeerr.ErrNotFound)
	require.EqualError(t, err, "target not found")

	input.Target = TargetInput{Kind: TargetKindTask, Ref: deletedTaskScopeID}
	_, err = service.ValidateForOwner(ctx, fixture.User.ID, fixture.WorkspaceID, input, "")
	require.ErrorIs(t, err, storeerr.ErrNotFound)
	require.EqualError(t, err, "target task not found")
}
