package socket

import (
	"context"
	"errors"
	"testing"
	"time"

	altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1"
	"git.toki-labs.com/toki/alt/packages/domain/market"
	"git.toki-labs.com/toki/alt/packages/domain/trading"
	protoSocket "git.toki-labs.com/toki/proto-socket/go"
)

type fakeLiveService struct {
	cap trading.BrokerCapability
	err error
}

func (f *fakeLiveService) GetLiveBrokerCapabilities(ctx context.Context, accountID string) (trading.BrokerCapability, error) {
	return f.cap, f.err
}

func sampleLiveCapability() trading.BrokerCapability {
	return trading.BrokerCapability{
		Broker:            trading.BrokerKIS,
		Markets:           map[market.Market]bool{market.MarketKR: true},
		Venues:            map[market.Venue]bool{market.VenueKRX: true},
		OrderTypes:        []trading.OrderType{trading.OrderTypeMarket, trading.OrderTypeLimit},
		SupportsCancel:    true,
		SupportsAccount:   true,
		SupportsAuditTags: false,
		CheckedAt:         time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC),
	}
}

func TestHandleGetLiveBrokerCapabilitiesSuccess(t *testing.T) {
	svc := &fakeLiveService{cap: sampleLiveCapability()}
	deps := Deps{Live: svc}

	resp, err := handleGetLiveBrokerCapabilities(deps, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp.GetError() != nil {
		t.Fatalf("unexpected typed error: %+v", resp.GetError())
	}
	cap := resp.GetBrokerCapabilities()
	if cap == nil {
		t.Fatal("expected broker capabilities in response")
	}
	if cap.GetBroker() != "kis" {
		t.Errorf("broker mismatch: %q", cap.GetBroker())
	}
	if !cap.GetSupportsCancel() {
		t.Error("expected supports_cancel to be true")
	}
}

func TestHandleGetLiveBrokerCapabilitiesUnavailable(t *testing.T) {
	deps := Deps{}

	resp, err := handleGetLiveBrokerCapabilities(deps, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"})
	if err != nil {
		t.Fatalf("expected typed unavailable response, got error: %v", err)
	}
	if resp.GetError() == nil {
		t.Fatal("expected error, got nil")
	}
	if resp.GetError().GetCode() != backtestErrorUnavailable {
		t.Errorf("expected unavailable code, got %q", resp.GetError().GetCode())
	}
}

func TestHandleGetLiveBrokerCapabilitiesEmptyAccountID(t *testing.T) {
	svc := &fakeLiveService{cap: sampleLiveCapability()}
	deps := Deps{Live: svc}

	resp, err := handleGetLiveBrokerCapabilities(deps, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: ""})
	if err != nil {
		t.Fatalf("expected typed invalid_request response, got error: %v", err)
	}
	if resp.GetError() == nil {
		t.Fatal("expected error, got nil")
	}
	if resp.GetError().GetCode() != backtestErrorInvalidRequest {
		t.Errorf("expected invalid_request code, got %q", resp.GetError().GetCode())
	}
	if resp.GetError().GetMessage() != "invalid live trading request: account_id is required" {
		t.Errorf("unexpected error message: %q", resp.GetError().GetMessage())
	}
}

func TestHandleGetLiveBrokerCapabilitiesServiceError(t *testing.T) {
	svc := &fakeLiveService{err: errors.New("broker connection failed")}
	deps := Deps{Live: svc}

	resp, err := handleGetLiveBrokerCapabilities(deps, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"})
	if err != nil {
		t.Fatalf("expected typed internal response, got error: %v", err)
	}
	if resp.GetError() == nil {
		t.Fatal("expected error, got nil")
	}
	if resp.GetError().GetCode() != backtestErrorInternal {
		t.Errorf("expected internal code, got %q", resp.GetError().GetCode())
	}
}

func TestLiveHandlersRegistration(t *testing.T) {
	registered := make(map[string]bool)
	for _, h := range liveHandlers(Deps{}) {
		if h.requestType == "" {
			t.Error("handler has empty request type")
		}
		if h.register == nil {
			t.Errorf("handler %q has nil register", h.requestType)
		}
		registered[h.requestType] = true
	}

	if !registered["alt.v1.GetLiveBrokerCapabilitiesRequest"] {
		t.Error("missing live capability handler registration")
	}
}

func TestLiveHandlersWithNilLiveService(t *testing.T) {
	// liveHandlers should still register even when deps.Live is nil.
	// The handler returns unavailable error at runtime.
	handlers := liveHandlers(Deps{})
	if len(handlers) == 0 {
		t.Error("expected live handlers to be registered even with nil Live service")
	}
}

func TestLiveSocketUnavailableReturnsTypedError(t *testing.T) {
	client := startBacktestWorkerTestClient(t, Deps{})

	resp, err := protoSocket.SendRequestTyped[*altv1.GetLiveBrokerCapabilitiesRequest, *altv1.GetLiveBrokerCapabilitiesResponse](
		&client.Communicator,
		&altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"},
		500*time.Millisecond,
	)
	if err != nil {
		t.Fatalf("request should return typed error response without timeout: %v", err)
	}
	if resp.GetError() == nil {
		t.Fatal("expected error, got nil")
	}
	if resp.GetError().GetCode() != backtestErrorUnavailable {
		t.Errorf("expected unavailable code, got %q", resp.GetError().GetCode())
	}
}

func TestLiveSocketRoundTripWithFakeService(t *testing.T) {
	svc := &fakeLiveService{cap: sampleLiveCapability()}
	client := startBacktestWorkerTestClient(t, Deps{Live: svc})

	resp, err := protoSocket.SendRequestTyped[*altv1.GetLiveBrokerCapabilitiesRequest, *altv1.GetLiveBrokerCapabilitiesResponse](
		&client.Communicator,
		&altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"},
		1*time.Second,
	)
	if err != nil {
		t.Fatalf("round trip failed: %v", err)
	}
	if resp.GetError() != nil {
		t.Fatalf("unexpected typed error: %+v", resp.GetError())
	}
	cap := resp.GetBrokerCapabilities()
	if cap == nil {
		t.Fatal("expected broker capabilities in response")
	}
	if cap.GetBroker() != "kis" {
		t.Errorf("broker mismatch: %q", cap.GetBroker())
	}
}

func TestLiveSocketEmptyAccountIDReturnsInvalidRequest(t *testing.T) {
	svc := &fakeLiveService{cap: sampleLiveCapability()}
	client := startBacktestWorkerTestClient(t, Deps{Live: svc})

	resp, err := protoSocket.SendRequestTyped[*altv1.GetLiveBrokerCapabilitiesRequest, *altv1.GetLiveBrokerCapabilitiesResponse](
		&client.Communicator,
		&altv1.GetLiveBrokerCapabilitiesRequest{AccountId: ""},
		500*time.Millisecond,
	)
	if err != nil {
		t.Fatalf("request should return typed error response without timeout: %v", err)
	}
	if resp.GetError() == nil {
		t.Fatal("expected error, got nil")
	}
	if resp.GetError().GetCode() != backtestErrorInvalidRequest {
		t.Errorf("expected invalid_request code, got %q", resp.GetError().GetCode())
	}
	if resp.GetBrokerCapabilities() != nil {
		t.Error("expected no broker capabilities when account_id is empty")
	}
}

func TestSessionHandlersIncludeLive(t *testing.T) {
	handlers := sessionHandlers(Deps{})
	seenLive := false
	for _, h := range handlers {
		if h.requestType == "alt.v1.GetLiveBrokerCapabilitiesRequest" {
			seenLive = true
			break
		}
	}
	if !seenLive {
		t.Error("sessionHandlers should include live capability handler")
	}
}
