// intakeharness_test.go — a fake bot API and a recorder for what the poller
// asked the daemon to do. Shared by the intake tests beside it.
package telegramsink

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
)

// fakeBot stands in for the bot API. It hands out queued updates once, records
// every other call, and answers the ok=true envelope the poller checks.
type fakeBot struct {
	mu      sync.Mutex
	updates [][]update // one batch per getUpdates call, then empty
	calls   []botCall
	srv     *httptest.Server
}

type botCall struct {
	method string
	body   map[string]any
}

func newFakeBot(t *testing.T, batches ...[]update) *fakeBot {
	t.Helper()
	b := &fakeBot{updates: batches}
	b.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		method := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:]
		raw, _ := io.ReadAll(r.Body)
		var body map[string]any
		_ = json.Unmarshal(raw, &body)

		b.mu.Lock()
		b.calls = append(b.calls, botCall{method: method, body: body})
		var batch []update
		if method == "getUpdates" && len(b.updates) > 0 {
			batch, b.updates = b.updates[0], b.updates[1:]
		}
		b.mu.Unlock()

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "result": batch})
	}))
	t.Cleanup(b.srv.Close)
	return b
}

func (b *fakeBot) called(method string) []botCall {
	b.mu.Lock()
	defer b.mu.Unlock()
	var out []botCall
	for _, c := range b.calls {
		if c.method == method {
			out = append(out, c)
		}
	}
	return out
}

// recorder collects what the poller asked the daemon to do.
type recorder struct {
	mu           sync.Mutex
	turns        []string
	conversation string
	traceID      int64
	corrections  []correction
	reply        string
	err          error
	correctErr   error
}

type correction struct {
	traceID  int64
	shouldBe string
}

func (r *recorder) turn(_ context.Context, conversation, text string) (string, int64, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.turns = append(r.turns, text)
	r.conversation = conversation
	return r.reply, r.traceID, r.err
}

func (r *recorder) correct(_ context.Context, traceID int64, shouldBe string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.corrections = append(r.corrections, correction{traceID, shouldBe})
	return r.correctErr
}

func (r *recorder) took() []string {
	r.mu.Lock()
	defer r.mu.Unlock()
	return append([]string(nil), r.turns...)
}

const ownerChat = "4242"

func newTestPoller(t *testing.T, b *fakeBot, rec *recorder) *Poller {
	t.Helper()
	sink, err := New(Config{BotToken: "secret-token", ChatID: ownerChat, BaseURL: b.srv.URL})
	if err != nil {
		t.Fatal(err)
	}
	p, err := NewPoller(sink, rec.turn, rec.correct)
	if err != nil {
		t.Fatal(err)
	}
	return p
}

func msg(chatID, text string) update {
	return update{UpdateID: 7, Message: &message{
		MessageID: 11, Text: text, Chat: chat{ID: json.Number(chatID)},
	}}
}
