package testdelivery

import (
	"context"
	"database/sql"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

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

	execer := &scriptedReadStateExecer{}
	now := time.Date(2026, 6, 7, 18, 0, 0, 0, time.UTC)
	err := advanceFixtureMessageReadState(context.Background(), execer, " mem-1 ", map[string]cursorUpdate{
		" scope-a ": {seq: 15, global: 150},
		" scope-b ": {seq: 20, global: 200},
		"scope-a":   {seq: 10, global: 100},
		"scope-z":   {seq: 0, global: 999},
		"":          {seq: 30, global: 300},
	}, now)
	if err != nil {
		t.Fatalf("advanceFixtureMessageReadState returned error: %v", err)
	}
	err = advanceFixtureActivityReadState(context.Background(), execer, " mem-1 ", map[string]cursorUpdate{
		" scope-c ": {global: 300},
		"scope-a":   {global: 100},
		"scope-z":   {seq: 99},
		"":          {global: 400},
	}, now)
	if err != nil {
		t.Fatalf("advanceFixtureActivityReadState returned error: %v", err)
	}

	if got, want := len(execer.execCalls), 2; got != want {
		t.Fatalf("exec call count = %d, want %d", got, want)
	}
	message := execer.execCalls[0]
	if strings.Count(message.query, "UPDATE scope_memberships") != 1 ||
		!strings.Contains(message.query, "scope_id IN ($3, $6)") ||
		!strings.Contains(message.query, "WHEN $3 THEN GREATEST(last_read_seq, $4)") ||
		!strings.Contains(message.query, "WHEN $6 THEN GREATEST(last_read_global_seq, $8)") {
		t.Fatalf("message cursor query was not batched as expected:\n%s", message.query)
	}
	if got, want := message.args, []any{"mem-1", now, "scope-a", int64(15), int64(150), "scope-b", int64(20), int64(200)}; !reflect.DeepEqual(got, want) {
		t.Fatalf("message cursor args = %#v, want %#v", got, want)
	}

	activity := execer.execCalls[1]
	if strings.Count(activity.query, "UPDATE scope_memberships") != 1 ||
		!strings.Contains(activity.query, "scope_id IN ($3, $5)") ||
		!strings.Contains(activity.query, "WHEN $3 THEN GREATEST(last_activity_global_seq, $4)") ||
		!strings.Contains(activity.query, "WHEN $5 THEN GREATEST(last_activity_global_seq, $6)") {
		t.Fatalf("activity cursor query was not batched as expected:\n%s", activity.query)
	}
	if got, want := activity.args, []any{"mem-1", now, "scope-a", int64(100), "scope-c", int64(300)}; !reflect.DeepEqual(got, want) {
		t.Fatalf("activity cursor args = %#v, want %#v", got, want)
	}
}

type scriptedReadStateExecer struct {
	execCalls []scriptedReadStateExecCall
}

func (execer *scriptedReadStateExecer) QueryRow(context.Context, string, ...any) coredb.Row {
	return scriptedReadStateRow{err: sql.ErrNoRows}
}

func (execer *scriptedReadStateExecer) Query(context.Context, string, ...any) (coredb.Rows, error) {
	return scriptedReadStateRows{}, nil
}

func (execer *scriptedReadStateExecer) Exec(_ context.Context, query string, args ...any) (coredb.Result, error) {
	execer.execCalls = append(execer.execCalls, scriptedReadStateExecCall{
		query: query,
		args:  append([]any(nil), args...),
	})
	return scriptedReadStateResult(1), nil
}

type scriptedReadStateExecCall struct {
	query string
	args  []any
}

type scriptedReadStateResult int64

func (result scriptedReadStateResult) RowsAffected() int64 {
	return int64(result)
}

type scriptedReadStateRow struct {
	err error
}

func (row scriptedReadStateRow) Scan(...any) error {
	return row.err
}

type scriptedReadStateRows struct{}

func (scriptedReadStateRows) Close()            {}
func (scriptedReadStateRows) Err() error        { return nil }
func (scriptedReadStateRows) Next() bool        { return false }
func (scriptedReadStateRows) Scan(...any) error { return sql.ErrNoRows }

func TestFanoutPayloadRecipientForMemberPrefersTeamContextRecipient(t *testing.T) {
	recipient, ok := fanoutPayloadRecipientForMember(message.ScopeFanoutPayload{
		Delivery: message.MessageDelivery{
			Recipients: []message.AgentRecipient{{
				AgentMemberID: "mem_agent",
				AgentID:       "agt_durable",
			}},
			WakeRecipients: []message.AgentRecipient{{
				AgentMemberID: "mem_agent",
				AgentID:       "agt_wake",
				TeamContexts: []message.TeamDeliveryContext{{
					TeamID:       "team_core",
					TeamMemberID: "wtm_agent",
				}},
			}},
		},
	}, " mem_agent ")

	if !ok {
		t.Fatal("expected recipient")
	}
	if got, want := recipient.AgentID, "agt_wake"; got != want {
		t.Fatalf("recipient.AgentID = %q, want %q", got, want)
	}
	if got, want := len(recipient.TeamContexts), 1; got != want {
		t.Fatalf("team context count = %d, want %d", got, want)
	}
}
