package fix

import (
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"quantex.com/qfixdpl/quickfix"
	"quantex.com/qfixdpl/src/app"
	"quantex.com/qfixdpl/src/domain"
)

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

// newTestManager builds a Manager with the given store without calling Start().
func newTestManager(store domain.PersistenceStore) *Manager {
	notify := &MockNotifier{}
	return NewManager(app.FIXConfig{}, store, notify)
}

// makeMsg builds a TradeMessage with the given quoteReqID, msgType, and body.
func makeMsg(quoteReqID, msgType string, body map[string]interface{}) domain.TradeMessage {
	return domain.TradeMessage{
		ID:         "test-id-" + quoteReqID + "-" + msgType,
		QuoteReqID: quoteReqID,
		JMessage: domain.FixMessageJSON{
			MsgType:     msgType,
			QuoteReqID:  quoteReqID,
			Body:        body,
			ReceiveTime: time.Now(),
		},
		CreatedAt: time.Now(),
	}
}

// makeQuoteRequest builds a "R" (QuoteRequest) TradeMessage.
func makeQuoteRequest(quoteReqID, listID, nt string, extras map[string]interface{}) domain.TradeMessage {
	body := map[string]interface{}{
		"NegotiationType": nt,
		"ListID":          listID,
	}
	for k, v := range extras {
		body[k] = v
	}
	return makeMsg(quoteReqID, "R", body)
}

// makeQuoteAck builds a "CW" (QuoteAck) TradeMessage.
func makeQuoteAck(quoteReqID, status string) domain.TradeMessage {
	return makeMsg(quoteReqID, "CW", map[string]interface{}{
		"QuoteAckStatus": status,
	})
}

// makeQuoteResponse builds an "AJ" (QuoteResponse) TradeMessage.
func makeQuoteResponse(quoteReqID, quoteRespID string) domain.TradeMessage {
	return makeMsg(quoteReqID, "AJ", map[string]interface{}{
		"QuoteRespID": quoteRespID,
	})
}

// makeExecutionReport builds an "8" (ExecutionReport) TradeMessage.
// clOrdID maps to body["ClOrdID"]; it is also set as TradeMessage.QuoteReqID
// to mirror how persistMessage works in handleExecutionReport.
func makeExecutionReport(clOrdID, execID string) domain.TradeMessage {
	return makeMsg(clOrdID, "8", map[string]interface{}{
		"ClOrdID": clOrdID,
		"ExecID":  execID,
	})
}

// makeOutgoing builds an outgoing message (AI, S, BN) for a given quoteReqID.
func makeOutgoing(quoteReqID, msgType string) domain.TradeMessage {
	return makeMsg(quoteReqID, msgType, map[string]interface{}{})
}

// ---------------------------------------------------------------------------
// Group 1 — DB vacia
// ---------------------------------------------------------------------------

func TestLoadTrades_EmptyDB(t *testing.T) {
	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.NotNil(t, m.trades)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 2 — Interrupcion despues de QuoteRequest
// ---------------------------------------------------------------------------

func TestLoadTrades_SingleR_CreatesOneTrade(t *testing.T) {
	r := makeQuoteRequest("LST_ABC123", "LIST_1", "RFQ", map[string]interface{}{
		"SecurityID":    "US1234567890",
		"Currency":      "USD",
		"Side":          "1",
		"OrderQty":      "1000000",
		"SettlDate":     "20260320",
		"OwnerTraderID": "trader1",
	})

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	require.Len(t, m.trades, 1)

	trade := m.trades["LST_ABC123"]
	require.NotNil(t, trade)
	assert.Equal(t, "LST_ABC123", trade.QuoteReqID)
	assert.Equal(t, "LIST_1", trade.ListID)
	assert.Equal(t, "US1234567890", trade.Symbol)
	assert.Equal(t, "USD", trade.Currency)
	assert.Equal(t, "1", string(trade.Side))
	assert.Equal(t, "20260320", trade.SettlDate)
	assert.Equal(t, "trader1", trade.OwnerTraderID)
	assert.Equal(t, domain.TradeStatusActive, trade.Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_R_WithOutgoingMsgs_IgnoresAI_S(t *testing.T) {
	r := makeQuoteRequest("LST_ABC123", "LIST_1", "RFQ", nil)
	ai := makeOutgoing("LST_ABC123", "AI")
	s := makeOutgoing("LST_ABC123", "S")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, ai, s}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_ABC123"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_R_NonLSTPrefix_Ignored(t *testing.T) {
	r := makeQuoteRequest("TRD_ABC123", "LIST_1", "RFQ", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

func TestLoadTrades_R_NonRFQ_NegotiationType_Ignored(t *testing.T) {
	r := makeQuoteRequest("LST_X", "LIST_1", "DEALER", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

func TestLoadTrades_R_EmptyListID_Ignored(t *testing.T) {
	r := makeQuoteRequest("LST_X", "", "RFQ", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

func TestLoadTrades_R_MissingOptionalFields_TradeCreated(t *testing.T) {
	r := makeQuoteRequest("LST_MIN", "LIST_MIN", "RFQ", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	require.Len(t, m.trades, 1)

	trade := m.trades["LST_MIN"]
	require.NotNil(t, trade)
	assert.Equal(t, "LST_MIN", trade.QuoteReqID)
	assert.Equal(t, "LIST_MIN", trade.ListID)
	assert.Equal(t, "", trade.Symbol)
	assert.Equal(t, "", trade.Currency)
	assert.Equal(t, "", trade.SettlDate)
	assert.Equal(t, "", trade.OwnerTraderID)
	assert.Equal(t, domain.TradeStatusActive, trade.Status)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 3 — Interrupcion despues de QuoteAck
// ---------------------------------------------------------------------------

func TestLoadTrades_CW_Accepted_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "1") // ACCEPTED

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_CW_Rejected_TradeMarkedRejected(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "2") // REJECTED

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusRejected, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

// QuoteAckStatus "0" is RECEIVED_NOT_YET_PROCESSED — not a rejection.
// Only status "2" (Rejected) should mark the trade as rejected.
func TestLoadTrades_CW_ReceivedNotYetProcessed_TradeStaysActive(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "0") // RECEIVED_NOT_YET_PROCESSED

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_CW_WithoutPriorR_NoCrash(t *testing.T) {
	cw := makeQuoteAck("LST_ORPHAN", "2")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{cw}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 4 — Interrupcion despues de QuoteResponse
// ---------------------------------------------------------------------------

func TestLoadTrades_AJ_TRDREQ_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "1")
	aj := makeQuoteResponse("LST_Q1", "LST_Q1_TRDREQ")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_AJ_TRDEND_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "1")
	aj := makeQuoteResponse("LST_Q1", "LST_Q1_TRDEND")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_AJ_LISTEND_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "1")
	aj := makeQuoteResponse("LST_Q1", "LST_Q1_LISTEND")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_AJ_TRDSUMM_TradeMarkedCompleted(t *testing.T) {
	r := makeQuoteRequest("LST_Q1", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_Q1", "1")
	aj := makeQuoteResponse("LST_Q1", "LST_Q1_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusCompleted, m.trades["LST_Q1"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_AJ_TRDSUMM_WithoutPriorR_NoCrash(t *testing.T) {
	aj := makeQuoteResponse("LST_ORPHAN", "LST_ORPHAN_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{aj}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 5 — Interrupcion despues de ExecutionReport
// ---------------------------------------------------------------------------

func TestLoadTrades_ExecReport_LISTEND_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_ABC123", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_ABC123", "1")
	aj := makeQuoteResponse("LST_ABC123", "LST_ABC123_TRDREQ")
	exec := makeExecutionReport("LST_ABC123", "LST_ABC123_LISTEND")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj, exec}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_ABC123"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_ExecReport_TRDEND_TradeRemains(t *testing.T) {
	r := makeQuoteRequest("LST_ABC123", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_ABC123", "1")
	aj := makeQuoteResponse("LST_ABC123", "LST_ABC123_TRDREQ")
	execListEnd := makeExecutionReport("LST_ABC123", "LST_ABC123_LISTEND")
	execTrdEnd := makeExecutionReport("LST_ABC123", "LST_ABC123_TRDEND")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj, execListEnd, execTrdEnd}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_ABC123"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_ExecReport_TRDSUMM_TradeMarkedCompleted(t *testing.T) {
	r := makeQuoteRequest("LST_ABC123", "LIST_1", "RFQ", nil)
	cw := makeQuoteAck("LST_ABC123", "1")
	aj := makeQuoteResponse("LST_ABC123", "LST_ABC123_TRDREQ")
	exec := makeExecutionReport("LST_ABC123", "LST_ABC123_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, cw, aj, exec}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 1)
	assert.Equal(t, domain.TradeStatusCompleted, m.trades["LST_ABC123"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_ExecReport_TRDSUMM_WithoutPriorR_NoCrash(t *testing.T) {
	exec := makeExecutionReport("LST_ORPHAN", "LST_ORPHAN_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{exec}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 6 — Multiples trades en paralelo
// ---------------------------------------------------------------------------

func TestLoadTrades_TwoTrades_BothActive(t *testing.T) {
	r1 := makeQuoteRequest("LST_TRADE1", "LIST_1", "RFQ", nil)
	r2 := makeQuoteRequest("LST_TRADE2", "LIST_2", "RFQ", nil)
	cw1 := makeQuoteAck("LST_TRADE1", "1")
	cw2 := makeQuoteAck("LST_TRADE2", "1")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r1, r2, cw1, cw2}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 2)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_TRADE1"].Status)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_TRADE2"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_TwoTrades_OneCompleted_OneActive(t *testing.T) {
	// TRADE1: fully completed via flow 8.4 (_TRDSUMM execution report)
	r1 := makeQuoteRequest("LST_TRADE1", "LIST_1", "RFQ", nil)
	cw1 := makeQuoteAck("LST_TRADE1", "1")
	aj1 := makeQuoteResponse("LST_TRADE1", "LST_TRADE1_TRDREQ")
	exec1 := makeExecutionReport("LST_TRADE1", "LST_TRADE1_TRDSUMM")

	// TRADE2: still active
	r2 := makeQuoteRequest("LST_TRADE2", "LIST_2", "RFQ", nil)
	cw2 := makeQuoteAck("LST_TRADE2", "1")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return(
		[]domain.TradeMessage{r1, cw1, aj1, exec1, r2, cw2}, nil,
	)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 2)
	assert.Equal(t, domain.TradeStatusCompleted, m.trades["LST_TRADE1"].Status)
	assert.Equal(t, domain.TradeStatusActive, m.trades["LST_TRADE2"].Status)
	store.AssertExpectations(t)
}

func TestLoadTrades_TwoTrades_BothCompleted(t *testing.T) {
	r1 := makeQuoteRequest("LST_TRADE1", "LIST_1", "RFQ", nil)
	cw1 := makeQuoteAck("LST_TRADE1", "1")
	exec1 := makeExecutionReport("LST_TRADE1", "LST_TRADE1_TRDSUMM")

	r2 := makeQuoteRequest("LST_TRADE2", "LIST_2", "RFQ", nil)
	cw2 := makeQuoteAck("LST_TRADE2", "1")
	exec2 := makeExecutionReport("LST_TRADE2", "LST_TRADE2_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return(
		[]domain.TradeMessage{r1, cw1, exec1, r2, cw2, exec2}, nil,
	)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	assert.Len(t, m.trades, 2)
	assert.Equal(t, domain.TradeStatusCompleted, m.trades["LST_TRADE1"].Status)
	assert.Equal(t, domain.TradeStatusCompleted, m.trades["LST_TRADE2"].Status)
	store.AssertExpectations(t)
}

// ---------------------------------------------------------------------------
// Group 7 — SessionID se asigna en onLogon (solo a trades activos)
// ---------------------------------------------------------------------------

func TestLoadTrades_SessionID_IsZeroAfterRecovery(t *testing.T) {
	r := makeQuoteRequest("LST_X", "LIST_1", "RFQ", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	err := m.loadTrades()

	require.NoError(t, err)
	require.Len(t, m.trades, 1)

	trade := m.trades["LST_X"]
	require.NotNil(t, trade)
	assert.Equal(t, quickfix.SessionID{}, trade.SessionID)
	store.AssertExpectations(t)
}

func TestOnLogon_AssignsSessionToRecoveredActiveTrades(t *testing.T) {
	r := makeQuoteRequest("LST_X", "LIST_1", "RFQ", nil)

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r}, nil)

	m := newTestManager(store)
	require.NoError(t, m.loadTrades())

	sessionID := quickfix.SessionID{
		BeginString:  "FIXT.1.1",
		SenderCompID: "QFIXDPL",
		TargetCompID: "TRADEWEB",
	}
	m.onLogon(sessionID)

	trade := m.trades["LST_X"]
	require.NotNil(t, trade)
	assert.Equal(t, sessionID, trade.SessionID)
}

func TestOnLogon_DoesNotAssignSessionToCompletedTrades(t *testing.T) {
	r := makeQuoteRequest("LST_X", "LIST_1", "RFQ", nil)
	exec := makeExecutionReport("LST_X", "LST_X_TRDSUMM")

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r, exec}, nil)

	m := newTestManager(store)
	require.NoError(t, m.loadTrades())

	sessionID := quickfix.SessionID{
		BeginString:  "FIXT.1.1",
		SenderCompID: "QFIXDPL",
		TargetCompID: "TRADEWEB",
	}
	m.onLogon(sessionID)

	trade := m.trades["LST_X"]
	require.NotNil(t, trade)
	assert.Equal(t, quickfix.SessionID{}, trade.SessionID, "completed trades should not get session assigned")
}

// ---------------------------------------------------------------------------
// Group 8 — GetTrades filtra por active, GetAllTrades devuelve todos
// ---------------------------------------------------------------------------

func TestGetTrades_ReturnsOnlyActive(t *testing.T) {
	r1 := makeQuoteRequest("LST_ACTIVE", "LIST_1", "RFQ", nil)
	r2 := makeQuoteRequest("LST_DONE", "LIST_2", "RFQ", nil)
	exec := makeExecutionReport("LST_DONE", "LST_DONE_TRDSUMM")
	r3 := makeQuoteRequest("LST_REJ", "LIST_3", "RFQ", nil)
	cw := makeQuoteAck("LST_REJ", "2") // REJECTED

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r1, r2, exec, r3, cw}, nil)

	m := newTestManager(store)
	require.NoError(t, m.loadTrades())

	trades := m.GetTrades()
	assert.Len(t, trades, 1)
	assert.Equal(t, "LST_ACTIVE", trades[0].QuoteReqID)
	assert.Equal(t, domain.TradeStatusActive, trades[0].Status)
}

func TestGetAllTrades_ReturnsAll(t *testing.T) {
	r1 := makeQuoteRequest("LST_ACTIVE", "LIST_1", "RFQ", nil)
	r2 := makeQuoteRequest("LST_DONE", "LIST_2", "RFQ", nil)
	exec := makeExecutionReport("LST_DONE", "LST_DONE_TRDSUMM")
	r3 := makeQuoteRequest("LST_REJ", "LIST_3", "RFQ", nil)
	cw := makeQuoteAck("LST_REJ", "2") // REJECTED

	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage{r1, r2, exec, r3, cw}, nil)

	m := newTestManager(store)
	require.NoError(t, m.loadTrades())

	trades := m.GetAllTrades()
	assert.Len(t, trades, 3)

	statusMap := map[string]domain.TradeStatus{}
	for _, tr := range trades {
		statusMap[tr.QuoteReqID] = tr.Status
	}

	assert.Equal(t, domain.TradeStatusActive, statusMap["LST_ACTIVE"])
	assert.Equal(t, domain.TradeStatusCompleted, statusMap["LST_DONE"])
	assert.Equal(t, domain.TradeStatusRejected, statusMap["LST_REJ"])
}

// ---------------------------------------------------------------------------
// Group 9 — Error en store
// ---------------------------------------------------------------------------

func TestLoadTrades_StoreError_ReturnsError_MapEmpty(t *testing.T) {
	store := &MockPersistenceStore{}
	store.On("GetTodayMessages").Return([]domain.TradeMessage(nil), errors.New("db connection failed"))

	m := newTestManager(store)
	err := m.loadTrades()

	assert.Error(t, err)
	assert.NotNil(t, m.trades)
	assert.Len(t, m.trades, 0)
	store.AssertExpectations(t)
}
