package postgres

import (
	"context"
	"fmt"
	"time"

	"github.com/genex/chain-indexer/internal/domain/entity"
	"github.com/genex/chain-indexer/internal/domain/repository"
	"gorm.io/gorm"
)

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

// chainTxModel is the GORM persistence model for the chain_transactions table.
type chainTxModel struct {
	Hash        string    `gorm:"column:hash;primaryKey"`
	BlockHeight int64     `gorm:"column:block_height;not null"`
	FromAddr    string    `gorm:"column:from_addr;not null"`
	ToAddr      string    `gorm:"column:to_addr;not null"`
	Amount      string    `gorm:"column:amount;not null;default:0"`
	Status      string    `gorm:"column:status;not null;default:confirmed"`
	CreatedAt   time.Time `gorm:"column:created_at;autoCreateTime"`
}

func (chainTxModel) TableName() string { return "chain_transactions" }

func (m *chainTxModel) toEntity() *entity.ChainTransaction {
	return &entity.ChainTransaction{
		Hash:        m.Hash,
		BlockHeight: m.BlockHeight,
		From:        m.FromAddr,
		To:          m.ToAddr,
		Amount:      m.Amount,
		Status:      m.Status,
		Timestamp:   m.CreatedAt,
	}
}

func chainTxFromEntity(e *entity.ChainTransaction) *chainTxModel {
	return &chainTxModel{
		Hash:        e.Hash,
		BlockHeight: e.BlockHeight,
		FromAddr:    e.From,
		ToAddr:      e.To,
		Amount:      e.Amount,
		Status:      e.Status,
	}
}

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

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

func (r *PostgresTransactionRepository) Save(ctx context.Context, tx *entity.ChainTransaction) error {
	if tx == nil {
		return fmt.Errorf("transaction must not be nil")
	}
	model := chainTxFromEntity(tx)
	return r.db.WithContext(ctx).Save(model).Error
}

func (r *PostgresTransactionRepository) SaveBatch(ctx context.Context, txs []*entity.ChainTransaction) error {
	if len(txs) == 0 {
		return nil
	}
	models := make([]chainTxModel, len(txs))
	for i, tx := range txs {
		models[i] = *chainTxFromEntity(tx)
	}
	return r.db.WithContext(ctx).Save(&models).Error
}

func (r *PostgresTransactionRepository) FindByHash(ctx context.Context, hash string) (*entity.ChainTransaction, error) {
	var model chainTxModel
	err := r.db.WithContext(ctx).Where("hash = ?", hash).First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresTransactionRepository) FindByBlock(ctx context.Context, blockHeight int64) ([]*entity.ChainTransaction, error) {
	var models []chainTxModel
	err := r.db.WithContext(ctx).Where("block_height = ?", blockHeight).Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.ChainTransaction, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}
