package repository import ( "context" "encoding/json" "fmt" "os" "strconv" "time" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/jackc/pgx/v5/pgtype" ) func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { if len(m.Odds) == 0 { fmt.Printf(" Market has no odds: %s (%s)\n", m.MarketType, m.EventID) return nil } for _, raw := range m.Odds { var item map[string]interface{} if err := json.Unmarshal(raw, &item); err != nil { fmt.Printf(" Invalid odd JSON for %s (%s): %v\n", m.MarketType, m.EventID, err) continue } header := getString(item["header"]) name := getString(item["name"]) handicap := getString(item["handicap"]) oddsVal := getFloat(item["odds"]) rawOddsBytes, _ := json.Marshal(m.Odds) params := dbgen.InsertNonLiveOddParams{ EventID: pgtype.Text{String: m.EventID, Valid: m.EventID != ""}, Fi: pgtype.Text{String: m.FI, Valid: m.FI != ""}, RawEventID: pgtype.Text{String: m.EventID, Valid: m.EventID != ""}, MarketType: m.MarketType, MarketName: pgtype.Text{String: m.MarketName, Valid: m.MarketName != ""}, MarketCategory: pgtype.Text{String: m.MarketCategory, Valid: m.MarketCategory != ""}, MarketID: pgtype.Text{String: m.MarketID, Valid: m.MarketID != ""}, Header: pgtype.Text{String: header, Valid: header != ""}, Name: pgtype.Text{String: name, Valid: name != ""}, Handicap: pgtype.Text{String: handicap, Valid: handicap != ""}, OddsValue: pgtype.Float8{Float64: oddsVal, Valid: oddsVal != 0}, Section: m.MarketCategory, Category: pgtype.Text{Valid: false}, RawOdds: rawOddsBytes, } err := s.queries.InsertNonLiveOdd(ctx, params) if err != nil { fmt.Printf(" Failed to insert odd for market %s (%s): %v\n", m.MarketType, m.EventID, err) _ = writeFailedMarketLog(m, err) continue } fmt.Printf("Inserted odd: %s | type=%s | header=%s | name=%s\n", m.EventID, m.MarketType, header, name) } return nil } func writeFailedMarketLog(m domain.Market, err error) error { logDir := "logs" logFile := logDir + "/failed_markets.log" if mkErr := os.MkdirAll(logDir, 0755); mkErr != nil { return mkErr } f, fileErr := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if fileErr != nil { return fileErr } defer f.Close() entry := struct { Time string `json:"time"` Error string `json:"error"` Record domain.Market `json:"record"` }{ Time: time.Now().Format(time.RFC3339), Error: err.Error(), Record: m, } jsonData, _ := json.MarshalIndent(entry, "", " ") _, writeErr := f.WriteString(string(jsonData) + "\n\n") return writeErr } func getString(v interface{}) string { if s, ok := v.(string); ok { return s } return "" } func getFloat(v interface{}) float64 { if s, ok := v.(string); ok { f, err := strconv.ParseFloat(s, 64) if err == nil { return f } } return 0 } func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { eventIDParam := pgtype.Text{String: eventID, Valid: eventID != ""} odds, err := s.queries.GetPrematchOdds(ctx, eventIDParam) if err != nil { return nil, err } domainOdds := make([]domain.Odd, len(odds)) for i, odd := range odds { domainOdds[i] = domain.Odd{ ID: int64(odd.ID), // Cast int32 to int64 EventID: odd.EventID.String, // Extract the String value Fi: odd.Fi.String, // Extract the String value RawEventID: odd.RawEventID.String, // Extract the String value MarketType: odd.MarketType, // Direct assignment MarketName: odd.MarketName.String, // Extract the String value MarketCategory: odd.MarketCategory.String, // Extract the String value MarketID: odd.MarketID.String, // Extract the String value Header: odd.Header.String, // Extract the String value Name: odd.Name.String, // Extract the String value Handicap: odd.Handicap.String, // Extract the String value OddsValue: odd.OddsValue.Float64, // Extract the Float64 value Section: odd.Section, // Direct assignment Category: odd.Category.String, // Extract the String value RawOdds: func() []domain.RawMessage { var rawOdds []domain.RawMessage if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil { rawOdds = nil } return rawOdds }(), FetchedAt: odd.FetchedAt.Time, // Extract the Time value Source: odd.Source.String, // Extract the String value IsActive: odd.IsActive.Bool, // Extract the Bool value } } return domainOdds, nil }