package m2mcore_test

import (
	"context"
	"testing"

	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
)

// makeResponse строит M2MTransferResponse сервиса МОСТ с заданным статусом и
// одним кодом/текстом (как в реальном ответе НРД, напр. M2M14).
func makeResponse(guid m2m.UUID, status m2m.StatusCode, code, text string) *m2m.M2MTransferResponse {
	return &m2m.M2MTransferResponse{
		GUID:       guid,
		StatusCode: status,
		Responses:  []m2m.Response{{Code: code, Text: text}},
	}
}

// TestReceiveServiceResponseError — ERROR (отказ сервиса, напр. M2M14)
// переводит сделку в Rejected и сохраняет ответ для отображения в карточке.
func TestReceiveServiceResponseError(t *testing.T) {
	d := newTestDeal(t)
	resp := makeResponse(d.GUID, m2m.StatusError, "M2M14",
		"Код ЭДО НРД отправителя отсутствует в справочнике участников M2M")

	if err := d.ReceiveServiceResponse(context.Background(), resp, nil); err != nil {
		t.Fatalf("ReceiveServiceResponse(ERROR): %v", err)
	}
	if d.State != m2mcore.StateRejected {
		t.Errorf("состояние %s, ожидалось %s", d.State, m2mcore.StateRejected)
	}
	if d.Response == nil || d.Response.StatusCode != m2m.StatusError {
		t.Errorf("ответ НРД не сохранён в сделке: %+v", d.Response)
	}
	if len(d.Response.Responses) == 0 || d.Response.Responses[0].Code != "M2M14" {
		t.Errorf("код ответа не сохранён: %+v", d.Response)
	}
}

// TestReceiveServiceResponseInfo — INFO (принято в обработку) сохраняет ответ,
// но НЕ меняет состояние: ждём M2MTransferDecision контрагента.
func TestReceiveServiceResponseInfo(t *testing.T) {
	d := newTestDeal(t)
	before := d.State
	resp := makeResponse(d.GUID, m2m.StatusInfo, "01", "Запрос на перевод принят в обработку")

	if err := d.ReceiveServiceResponse(context.Background(), resp, nil); err != nil {
		t.Fatalf("ReceiveServiceResponse(INFO): %v", err)
	}
	if d.State != before {
		t.Errorf("состояние изменилось на %s, ожидалось без изменений (%s)", d.State, before)
	}
	if d.Response == nil || d.Response.StatusCode != m2m.StatusInfo {
		t.Errorf("ответ НРД не сохранён: %+v", d.Response)
	}
}

// TestReceiveServiceResponseTerminalIdempotent — повторный ERROR на уже
// терминальной (Rejected) сделке не валит FSM, ответ обновляется.
func TestReceiveServiceResponseTerminalIdempotent(t *testing.T) {
	d := newTestDeal(t)
	first := makeResponse(d.GUID, m2m.StatusError, "M2M14", "первый отказ")
	if err := d.ReceiveServiceResponse(context.Background(), first, nil); err != nil {
		t.Fatalf("первый ERROR: %v", err)
	}
	// Поллер ИШ может прислать тот же пакет повторно — не должно паниковать
	// и не должно ломать состояние недопустимым переходом Rejected→Rejected.
	second := makeResponse(d.GUID, m2m.StatusError, "M2M14", "повторный отказ")
	if err := d.ReceiveServiceResponse(context.Background(), second, nil); err != nil {
		t.Fatalf("повторный ERROR на терминальной сделке: %v", err)
	}
	if d.State != m2mcore.StateRejected {
		t.Errorf("состояние %s, ожидалось %s", d.State, m2mcore.StateRejected)
	}
}

// TestReceiveServiceResponseNil — защита от nil.
func TestReceiveServiceResponseNil(t *testing.T) {
	d := newTestDeal(t)
	if err := d.ReceiveServiceResponse(context.Background(), nil, nil); err == nil {
		t.Error("ожидалась ошибка на nil-ответе")
	}
}
