package repository import ( "context" "fmt" "time" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/jackc/pgx/v5/pgtype" ) type ReportRepository interface { GenerateReport(timeFrame domain.TimeFrame, start, end time.Time) (*domain.Report, error) SaveReport(report *domain.Report) error FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error) GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error) GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) } type ReportRepo struct { store *Store } func NewReportRepo(store *Store) ReportRepository { return &ReportRepo{store: store} } func (r *ReportRepo) GenerateReport(timeFrame domain.TimeFrame, start, end time.Time) (*domain.Report, error) { // Implement SQL queries to calculate metrics var report domain.Report // Total Bets err := r.store.conn.QueryRow( context.Background(), `SELECT COUNT(*) FROM bets WHERE created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalBets) if err != nil { return nil, err } // Total Cash In err = r.store.conn.QueryRow( context.Background(), `SELECT COALESCE(SUM(amount), 0) FROM transactions WHERE type = 'stake' AND created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalCashIn) if err != nil { return nil, err } // Similar queries for Cash Out and Cash Back... report.TimeFrame = timeFrame report.PeriodStart = start report.PeriodEnd = end report.GeneratedAt = time.Now() return &report, nil } func (r *ReportRepo) SaveReport(report *domain.Report) error { _, err := r.store.conn.Exec( context.Background(), `INSERT INTO reports (id, time_frame, period_start, period_end, total_bets, total_cash_in, total_cash_out, total_cash_back, generated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, report.ID, report.TimeFrame, report.PeriodStart, report.PeriodEnd, report.TotalBets, report.TotalCashIn, report.TotalCashOut, report.TotalCashBack, report.GeneratedAt) return err } func (r *ReportRepo) FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error) { rows, err := r.store.conn.Query( context.Background(), `SELECT id, time_frame, period_start, period_end, total_bets, total_cash_in, total_cash_out, total_cash_back, generated_at FROM reports WHERE time_frame = $1 ORDER BY generated_at DESC LIMIT $2`, timeFrame, limit) if err != nil { return nil, err } defer rows.Close() var reports []*domain.Report for rows.Next() { var report domain.Report err := rows.Scan( &report.ID, &report.TimeFrame, &report.PeriodStart, &report.PeriodEnd, &report.TotalBets, &report.TotalCashIn, &report.TotalCashOut, &report.TotalCashBack, &report.GeneratedAt, ) if err != nil { return nil, err } reports = append(reports, &report) } if err := rows.Err(); err != nil { return nil, err } return reports, nil } func (r *ReportRepo) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) { params := dbgen.GetTotalBetsMadeInRangeParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } return r.store.queries.GetTotalBetsMadeInRange(ctx, params) } func (r *ReportRepo) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) { params := dbgen.GetTotalCashBacksInRangeParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } value, err := r.store.queries.GetTotalCashBacksInRange(ctx, params) if err != nil { return 0, err } return parseFloat(value) } func (r *ReportRepo) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) { params := dbgen.GetTotalCashMadeInRangeParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } value, err := r.store.queries.GetTotalCashMadeInRange(ctx, params) if err != nil { return 0, err } return parseFloat(value) } func (r *ReportRepo) GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) { params := dbgen.GetTotalCashOutInRangeParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } value, err := r.store.queries.GetTotalCashOutInRange(ctx, params) if err != nil { return 0, err } return parseFloat(value) } func (r *ReportRepo) GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) { params := dbgen.GetWalletTransactionsInRangeParams{ CreatedAt: ToPgTimestamp(from), CreatedAt_2: ToPgTimestamp(to), } return r.store.queries.GetWalletTransactionsInRange(ctx, params) } func (r *ReportRepo) GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error) { params := dbgen.GetAllTicketsInRangeParams{ CreatedAt: ToPgTimestamp(from), CreatedAt_2: ToPgTimestamp(to), } return r.store.queries.GetAllTicketsInRange(ctx, params) } func (r *ReportRepo) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) { params := dbgen.GetVirtualGameSummaryInRangeParams{ CreatedAt: ToPgTimestamptz(from), CreatedAt_2: ToPgTimestamptz(to), } return r.store.queries.GetVirtualGameSummaryInRange(ctx, params) } func ToPgTimestamp(t time.Time) pgtype.Timestamp { return pgtype.Timestamp{Time: t, Valid: true} } func ToPgTimestamptz(t time.Time) pgtype.Timestamptz { return pgtype.Timestamptz{Time: t, Valid: true} } func parseFloat(value interface{}) (float64, error) { switch v := value.(type) { case float64: return v, nil case int64: return float64(v), nil case pgtype.Numeric: if !v.Valid { return 0, nil } f, err := v.Float64Value() if err != nil { return 0, fmt.Errorf("failed to convert pgtype.Numeric to float64: %w", err) } return f.Float64, nil case nil: return 0, nil default: return 0, fmt.Errorf("unexpected type %T for value: %+v", v, v) } } func (r *ReportRepo) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) { params := dbgen.GetCompanyWiseReportParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } return r.store.queries.GetCompanyWiseReport(ctx, params) } func (r *ReportRepo) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) { params := dbgen.GetBranchWiseReportParams{ From: ToPgTimestamp(from), To: ToPgTimestamp(to), } return r.store.queries.GetBranchWiseReport(ctx, params) }