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: PostgresBlockRepository implements repository.BlockRepository.
var _ repository.BlockRepository = (*PostgresBlockRepository)(nil)

// blockModel is the GORM persistence model for the blocks table.
type blockModel struct {
	Height    int64     `gorm:"column:height;primaryKey"`
	Hash      string    `gorm:"column:hash;not null;uniqueIndex"`
	TxCount   int       `gorm:"column:tx_count;not null;default:0"`
	IndexedAt time.Time `gorm:"column:indexed_at;autoCreateTime"`
	CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
}

func (blockModel) TableName() string { return "blocks" }

func (m *blockModel) toEntity() *entity.Block {
	return &entity.Block{
		Height:    m.Height,
		Hash:      m.Hash,
		Timestamp: m.IndexedAt,
		TxCount:   m.TxCount,
	}
}

func blockFromEntity(e *entity.Block) *blockModel {
	return &blockModel{
		Height:    e.Height,
		Hash:      e.Hash,
		TxCount:   e.TxCount,
		IndexedAt: e.Timestamp,
	}
}

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

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

func (r *PostgresBlockRepository) SaveBlock(ctx context.Context, block *entity.Block) error {
	if block == nil {
		return fmt.Errorf("block must not be nil")
	}
	model := blockFromEntity(block)
	return r.db.WithContext(ctx).Save(model).Error
}

func (r *PostgresBlockRepository) FindByHeight(ctx context.Context, height int64) (*entity.Block, error) {
	var model blockModel
	err := r.db.WithContext(ctx).Where("height = ?", height).First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresBlockRepository) FindLatest(ctx context.Context) (*entity.Block, error) {
	var model blockModel
	err := r.db.WithContext(ctx).Order("height DESC").First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresBlockRepository) FindRange(ctx context.Context, fromHeight, toHeight int64) ([]*entity.Block, error) {
	var models []blockModel
	err := r.db.WithContext(ctx).
		Where("height >= ? AND height <= ?", fromHeight, toHeight).
		Order("height ASC").
		Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.Block, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}

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