package mock_test

import (
	"context"
	"testing"
	"time"

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

func validRequest() *m2m.M2MTransferRequest {
	whole := uint64(100)
	isin := m2m.ISIN("RU0007661625")
	return &m2m.M2MTransferRequest{
		Header: m2m.RequestHeader{
			GUID:         m2m.UUID("c02a1d5e-c2af-4799-bab4-953f133c5133"),
			SenderCode:   "MC0079200000",
			ReceiverCode: "MC0010300000",
			CostInfo:     m2m.CostInfo{No: &m2m.CostInfoNo{}},
		},
		Data: m2m.RequestData{
			InvestorInformation: m2m.InvestorInformation{
				LastName:  "Иванов",
				FirstName: "Иван",
				IdentityDocument: m2m.IdentityDocument{
					DocumentType:   m2m.DocCode21,
					DocumentNumber: m2m.IdentityDocSerial("654321"),
				},
			},
			TransferringDepository: m2m.SettlementRequisites{INN: "7702070139"},
			ReceivingDepository:    m2m.SettlementRequisites{INN: "7802031669"},
			TransferredSecurities: m2m.RequestTransferredSecurities{
				Securities: []m2m.RequestSecurity{
					{
						ReferenceID:     "M2M2026030200001",
						SecurityCode:    "MM0766162534",
						SecurityDetails: m2m.SecurityDetails{ISIN: &isin},
						Quantity:        m2m.Quantity{Whole: &whole},
						SettlementAccount: []m2m.RequestSettlementAccount{
							{
								SettlementRequisites: m2m.SettlementRequisites{INN: "7702070139"},
								SettlementLocation: m2m.SettlementDepositoryLocation{
									DeponentCode: "DP789456",
									AccountID:    "31MC0021900000F01",
									SectionID:    "P001",
								},
							},
						},
						IsolationStatus: m2m.IsolationSGDN,
					},
				},
			},
		},
	}
}

func TestMockSendReturnsResponseAndEmitsConfirmation(t *testing.T) {
	cfg := mock.DefaultConfig()
	cfg.DecisionDelay = 20 * time.Millisecond
	s := mock.NewSender(cfg)

	resp, err := s.Send(context.Background(), validRequest())
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != m2m.StatusInfo {
		t.Errorf("StatusCode = %s, ожидалось INFO", resp.StatusCode)
	}
	if len(resp.Responses) != 1 {
		t.Errorf("ожидалась 1 Response, получено %d", len(resp.Responses))
	}

	select {
	case d := <-s.Decisions():
		if d.Data.Securities[0].TransferDecision.Confirmation == nil {
			t.Errorf("ожидалось Confirmation, получено %+v", d.Data.Securities[0].TransferDecision)
		}
	case <-time.After(time.Second):
		t.Fatal("Decision не пришёл в канал")
	}
}

func TestMockRejectOutcome(t *testing.T) {
	cfg := mock.DefaultConfig()
	cfg.DecisionDelay = 20 * time.Millisecond
	cfg.DefaultOutcome = mock.OutcomeReject
	s := mock.NewSender(cfg)
	req := validRequest()
	if _, err := s.Send(context.Background(), req); err != nil {
		t.Fatal(err)
	}
	select {
	case d := <-s.Decisions():
		if d.Data.Securities[0].TransferDecision.Rejection == nil {
			t.Errorf("ожидалось Rejection")
		}
	case <-time.After(time.Second):
		t.Fatal("Decision не пришёл")
	}
}

func TestMockTimeoutOutcome(t *testing.T) {
	cfg := mock.DefaultConfig()
	cfg.DecisionDelay = 20 * time.Millisecond
	cfg.DefaultOutcome = mock.OutcomeTimeout
	s := mock.NewSender(cfg)
	if _, err := s.Send(context.Background(), validRequest()); err != nil {
		t.Fatal(err)
	}
	select {
	case <-s.Decisions():
		t.Error("при OutcomeTimeout Decision не должен прилетать")
	case <-time.After(100 * time.Millisecond):
	}
	_, _, _, timedOut := s.Stats()
	if timedOut != 1 {
		t.Errorf("счётчик timeout = %d, ожидалось 1", timedOut)
	}
}

func TestMockSetOutcomeOverride(t *testing.T) {
	cfg := mock.DefaultConfig()
	cfg.DecisionDelay = 20 * time.Millisecond
	cfg.DefaultOutcome = mock.OutcomeConfirm
	s := mock.NewSender(cfg)
	req := validRequest()
	s.SetOutcome(req.Header.GUID, mock.OutcomeReject)
	if _, err := s.Send(context.Background(), req); err != nil {
		t.Fatal(err)
	}
	select {
	case d := <-s.Decisions():
		if d.Data.Securities[0].TransferDecision.Rejection == nil {
			t.Errorf("override не сработал, ожидалось Rejection")
		}
	case <-time.After(time.Second):
		t.Fatal("Decision не пришёл")
	}
}
