package main

import (
	"fmt"
	"git.beejay.kim/Gshopper/sentio"
	"github.com/samber/lo"
	"reflect"
	"slices"
	"testing"
	"time"
)

func Test__ibkr_IsOpen(t *testing.T) {
	tests := []struct {
		time string
		want bool
	}{
		{
			time: "2024-08-26T13:29:59Z", // 1s before open
			want: false,
		},
		{
			time: "2024-08-02T15:04:05Z", // Opened
			want: true,
		},
		{
			time: "2024-08-03T18:34:00Z", // Saturday
			want: false,
		},
		{
			time: "2024-08-25T15:00:00Z", // Sunday
			want: false,
		},
		{
			time: "2024-08-26T19:00:05Z", // Closed
			want: false,
		},
	}

	for _, tt := range tests {
		var (
			c   ClockFixed
			s   _ibkr
			err error
		)

		if c.fix, err = time.ParseInLocation(time.RFC3339, tt.time, time.UTC); err != nil {
			t.Errorf("ParseInLocation() = %v", err)
			return
		}

		s = _ibkr{
			clock: c,
		}

		t.Run(fmt.Sprintf("IBKR:IsOpen:%s", tt.time), func(t *testing.T) {
			got := s.IsOpen()
			if got != tt.want {
				t.Errorf("IsOpen() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test__ibkr_ShouldClosePositions(t *testing.T) {
	type args struct {
		portfolio sentio.Portfolio
		proba     float64
		time      string
	}

	tests := []struct {
		args args
		want []string
	}{
		{ // should close SHORT position
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "569311092",
						size:   10,
						price:  8,
					},
				),
				proba: 1.00001,
				time:  "2024-08-02T15:04:05Z",
			},
			want: []string{"569311092"},
		},
		{ // should close LONG position
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "569311092",
						size:   10,
						price:  8,
					},
				),
				proba: .9,
				time:  "2024-08-02T15:04:05Z",
			},
			want: []string{"521525019"},
		},
		{ // nothing to close
			args: args{
				portfolio: newStubPortfolio(),
				proba:     .9,
				time:      "2024-08-02T15:04:05Z",
			},
			want: nil,
		},
		{ // should close both positions
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "521525020",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "569311092",
						size:   10,
						price:  8,
					},
				),
				proba: .9,
				time:  "2024-08-02T19:00:05Z",
			},
			want: []string{"521525019", "569311092"},
		},
		{ // should close nothing
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "569311092",
						size:   10,
						price:  8,
					},
					PositionStub{
						symbol: "521525020",
						size:   10,
						price:  20,
					},
				),
				proba: .9,
				time:  "2024-08-02T18:00:05Z",
			},
			want: nil,
		},
		{ // should close nothing
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "521525020",
						size:   10,
						price:  20,
					},
				),
				proba: 1.2,
				time:  "2024-08-02T18:00:05Z",
			},
			want: nil,
		},
		{ // should close all
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   10,
						price:  20,
					},
					PositionStub{
						symbol: "521525020",
						size:   10,
						price:  20,
					},
				),
				proba: 1.2,
				time:  "2024-08-02T12:00:05Z",
			},
			want: []string{"521525019"},
		},
		{ // should close nothing
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525020",
						size:   10,
						price:  20,
					},
				),
				proba: 1.1,
				time:  "2024-08-02T12:00:05Z",
			},
			want: nil,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "569311092",
						size:   10938,
						price:  8.24685045,
					}),
				proba: 0.9972525825782994,
				time:  "2024-09-03T04:00:00Z",
			},
			want: []string{"569311092"},
		},
	}

	for _, tt := range tests {
		var (
			c   ClockFixed
			s   _ibkr
			err error
		)

		if c.fix, err = time.ParseInLocation(time.RFC3339, tt.args.time, time.UTC); err != nil {
			t.Errorf("ParseInLocation() = %v", err)
			return
		}

		s = _ibkr{
			clock: c,
		}

		t.Run("IBKR:ShouldClosePositions", func(t *testing.T) {
			if got := s.ShouldClosePositions(tt.args.portfolio, tt.args.proba); !slices.Equal(got, tt.want) {
				t.Errorf("ShouldClosePositions() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test__ibkr_ShouldOpenPosition(t *testing.T) {
	type args struct {
		portfolio sentio.Portfolio
		proba     float64
		time      string
	}

	tests := []struct {
		args   args
		symbol *string
		size   float64
	}{
		{
			args: args{
				portfolio: newStubPortfolio(),
				proba:     1.00201,
				time:      "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("521525019"),
			size:   .6,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   1,
						price:  19,
					},
				),
				proba: 1.00201,
				time:  "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("521525019"),
			size:   .3,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525019",
						size:   -.2,
						price:  19,
					},
				),
				proba: 1.00201,
				time:  "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("521525019"),
			size:   .6,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525020",
						size:   3,
						price:  19,
					},
				),
				proba: 1.00201,
				time:  "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("521525019"),
			size:   .6,
		},
		{
			args: args{
				portfolio: newStubPortfolio(),
				proba:     .9979,
				time:      "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("569311092"),
			size:   .8,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "569311092",
						size:   100,
						price:  8,
					},
				),
				proba: .9979,
				time:  "2024-08-02T18:30:00Z",
			},
			symbol: lo.ToPtr("569311092"),
			size:   .3,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "521525020",
						size:   100,
						price:  8,
					},
				),
				proba: .9979,
				time:  "2024-08-02T19:00:00Z",
			},
			symbol: nil,
			size:   0,
		},
		{
			args: args{
				portfolio: newStubPortfolio(
					PositionStub{
						symbol: "569311092",
						size:   100,
						price:  8,
					},
				),
				proba: -1,
				time:  "2024-08-02T18:30:00Z",
			},
			symbol: nil,
			size:   0,
		},
	}

	for _, tt := range tests {
		t.Run("IBKR:ShouldOpenPosition", func(t *testing.T) {
			var (
				c   ClockFixed
				s   _ibkr
				err error
			)

			if c.fix, err = time.ParseInLocation(time.RFC3339, tt.args.time, time.UTC); err != nil {
				t.Errorf("ParseInLocation() = %v", err)
				return
			}

			s = _ibkr{
				clock: c,
			}

			symbol, size := s.ShouldOpenPosition(tt.args.portfolio, tt.args.proba)
			if !reflect.DeepEqual(symbol, tt.symbol) {
				t.Errorf("ShouldOpenPosition() symbol = %v, want %v", symbol, tt.symbol)
			}

			if size != tt.size {
				t.Errorf("ShouldOpenPosition() size = %v, want %v", size, tt.size)
			}
		})
	}
}