package repository

import (
	"context"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/kjannette/trahn-backend/internal/models"
)

type PriceRepo struct {
	pool *pgxpool.Pool
}

func NewPriceRepo(pool *pgxpool.Pool) *PriceRepo {
	return &PriceRepo{pool: pool}
}

func (r *PriceRepo) Record(ctx context.Context, price float64, ts time.Time) (*models.PricePoint, error) {
	td := TradingDay(ts)
	row := r.pool.QueryRow(ctx,
		`INSERT INTO price_history (timestamp, price, trading_day, source)
		 VALUES ($1, $2, $3, $4) RETURNING *`,
		ts, price, td, "coingecko",
	)
	return scanPrice(row)
}

func (r *PriceRepo) GetByDay(ctx context.Context, tradingDay string) ([]models.PricePoint, error) {
	rows, err := r.pool.Query(ctx,
		`SELECT * FROM price_history WHERE trading_day = $1 ORDER BY timestamp ASC`,
		tradingDay,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return collectPrices(rows)
}

func (r *PriceRepo) GetAvailableDays(ctx context.Context) ([]string, error) {
	rows, err := r.pool.Query(ctx,
		`SELECT DISTINCT trading_day FROM price_history ORDER BY trading_day DESC LIMIT 30`,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var days []string
	for rows.Next() {
		var d time.Time
		if err := rows.Scan(&d); err != nil {
			return nil, err
		}
		days = append(days, d.Format("2006-01-02"))
	}
	return days, rows.Err()
}

func (r *PriceRepo) GetLatest(ctx context.Context) (*models.PricePoint, error) {
	row := r.pool.QueryRow(ctx,
		`SELECT * FROM price_history ORDER BY timestamp DESC LIMIT 1`,
	)
	p, err := scanPrice(row)
	if err != nil {
		if err.Error() == "no rows in result set" {
			return nil, nil
		}
		return nil, err
	}
	return p, nil
}

// --- scan helpers ---

type scannable interface {
	Scan(dest ...any) error
}

func scanPrice(row scannable) (*models.PricePoint, error) {
	var p models.PricePoint
	var td time.Time
	err := row.Scan(&p.ID, &p.Timestamp, &p.Price, &td, &p.Source, &p.CreatedAt)
	if err != nil {
		return nil, err
	}
	p.TradingDay = td.Format("2006-01-02")
	return &p, nil
}

type rowsIter interface {
	Next() bool
	Scan(dest ...any) error
	Err() error
}

func collectPrices(rows rowsIter) ([]models.PricePoint, error) {
	var out []models.PricePoint
	for rows.Next() {
		var p models.PricePoint
		var td time.Time
		if err := rows.Scan(&p.ID, &p.Timestamp, &p.Price, &td, &p.Source, &p.CreatedAt); err != nil {
			return nil, err
		}
		p.TradingDay = td.Format("2006-01-02")
		out = append(out, p)
	}
	return out, rows.Err()
}
