package postgres

import (
	"context"
	"fmt"
	"time"

	"github.com/genex/trading-service/internal/domain/entity"
	"github.com/genex/trading-service/internal/domain/repository"
	"github.com/genex/trading-service/internal/domain/vo"
	"gorm.io/gorm"
)

// Compile-time check: PostgresTradeRepository implements repository.TradeRepository.
var _ repository.TradeRepository = (*PostgresTradeRepository)(nil)

// tradeModel is the GORM persistence model for the trades table.
type tradeModel struct {
	ID          string    `gorm:"column:id;primaryKey"`
	BuyOrderID  string    `gorm:"column:buy_order_id;not null"`
	SellOrderID string    `gorm:"column:sell_order_id;not null"`
	CouponID    string    `gorm:"column:coupon_id;not null"`
	BuyerID     string    `gorm:"column:buyer_id;not null"`
	SellerID    string    `gorm:"column:seller_id;not null"`
	Price       float64   `gorm:"column:price;not null"`
	Quantity    int       `gorm:"column:quantity;not null;default:1"`
	BuyerFee    float64   `gorm:"column:buyer_fee;not null;default:0"`
	SellerFee   float64   `gorm:"column:seller_fee;not null;default:0"`
	Status      string    `gorm:"column:status;not null;default:pending"`
	TxHash      *string   `gorm:"column:tx_hash"`
	SettledAt   *time.Time `gorm:"column:settled_at"`
	CreatedAt   time.Time `gorm:"column:created_at;autoCreateTime"`
}

func (tradeModel) TableName() string { return "trades" }

func (m *tradeModel) toEntity() *entity.Trade {
	return &entity.Trade{
		ID:          m.ID,
		CouponID:    m.CouponID,
		BuyOrderID:  m.BuyOrderID,
		SellOrderID: m.SellOrderID,
		BuyerID:     m.BuyerID,
		SellerID:    m.SellerID,
		Price:       vo.MustNewPrice(m.Price),
		Quantity:    m.Quantity,
		BuyerFee:    m.BuyerFee,
		SellerFee:   m.SellerFee,
		CreatedAt:   m.CreatedAt,
	}
}

func tradeFromEntity(e *entity.Trade) *tradeModel {
	return &tradeModel{
		ID:          e.ID,
		BuyOrderID:  e.BuyOrderID,
		SellOrderID: e.SellOrderID,
		CouponID:    e.CouponID,
		BuyerID:     e.BuyerID,
		SellerID:    e.SellerID,
		Price:       e.Price.Float64(),
		Quantity:    e.Quantity,
		BuyerFee:    e.BuyerFee,
		SellerFee:   e.SellerFee,
		Status:      "pending",
		CreatedAt:   e.CreatedAt,
	}
}

// PostgresTradeRepository is the GORM-backed implementation of repository.TradeRepository.
type PostgresTradeRepository struct {
	db *gorm.DB
}

// NewPostgresTradeRepository creates a new repository backed by PostgreSQL via GORM.
func NewPostgresTradeRepository(db *gorm.DB) *PostgresTradeRepository {
	return &PostgresTradeRepository{db: db}
}

func (r *PostgresTradeRepository) Save(ctx context.Context, trade *entity.Trade) error {
	if trade == nil {
		return fmt.Errorf("trade must not be nil")
	}
	model := tradeFromEntity(trade)
	return r.db.WithContext(ctx).Save(model).Error
}

func (r *PostgresTradeRepository) FindByID(ctx context.Context, id string) (*entity.Trade, error) {
	var model tradeModel
	err := r.db.WithContext(ctx).Where("id = ?", id).First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("trade not found: %s", id)
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresTradeRepository) FindByOrderID(ctx context.Context, orderID string) ([]*entity.Trade, error) {
	var models []tradeModel
	err := r.db.WithContext(ctx).
		Where("buy_order_id = ? OR sell_order_id = ?", orderID, orderID).
		Order("created_at DESC").
		Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.Trade, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}

func (r *PostgresTradeRepository) FindRecent(ctx context.Context, limit int) ([]*entity.Trade, error) {
	var models []tradeModel
	err := r.db.WithContext(ctx).Order("created_at DESC").Limit(limit).Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.Trade, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}

func (r *PostgresTradeRepository) FindByCouponID(ctx context.Context, couponID string) ([]*entity.Trade, error) {
	var models []tradeModel
	err := r.db.WithContext(ctx).Where("coupon_id = ?", couponID).Order("created_at DESC").Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.Trade, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}

func (r *PostgresTradeRepository) Count(ctx context.Context) (int64, error) {
	var count int64
	err := r.db.WithContext(ctx).Model(&tradeModel{}).Count(&count).Error
	return count, err
}
