package postgres

import (
	"context"
	"fmt"
	"time"

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

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

// addressMappingModel is the GORM persistence model for the address_mappings table.
// It lives in the infrastructure layer, keeping the domain entity free of ORM concerns.
type addressMappingModel struct {
	ID              string    `gorm:"column:id;primaryKey"`
	UserID          string    `gorm:"column:user_id;not null"`
	InternalAddress string    `gorm:"column:internal_address;not null;uniqueIndex"`
	ChainAddress    string    `gorm:"column:chain_address;not null"`
	ChainType       string    `gorm:"column:chain_type;not null"`
	IsActive        bool      `gorm:"column:is_active;not null;default:true"`
	CreatedAt       time.Time `gorm:"column:created_at;autoCreateTime"`
	UpdatedAt       time.Time `gorm:"column:updated_at;autoUpdateTime"`
}

func (addressMappingModel) TableName() string { return "address_mappings" }

func (m *addressMappingModel) toEntity() *entity.AddressMapping {
	return &entity.AddressMapping{
		ID:              m.ID,
		UserID:          m.UserID,
		InternalAddress: m.InternalAddress,
		ChainAddress:    m.ChainAddress,
		ChainType:       m.ChainType,
		IsActive:        m.IsActive,
		CreatedAt:       m.CreatedAt,
		UpdatedAt:       m.UpdatedAt,
	}
}

func fromEntity(e *entity.AddressMapping) *addressMappingModel {
	return &addressMappingModel{
		ID:              e.ID,
		UserID:          e.UserID,
		InternalAddress: e.InternalAddress,
		ChainAddress:    e.ChainAddress,
		ChainType:       e.ChainType,
		IsActive:        e.IsActive,
		CreatedAt:       e.CreatedAt,
		UpdatedAt:       e.UpdatedAt,
	}
}

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

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

func (r *PostgresAddressMappingRepository) Save(ctx context.Context, mapping *entity.AddressMapping) error {
	if mapping == nil {
		return fmt.Errorf("mapping must not be nil")
	}
	model := fromEntity(mapping)
	return r.db.WithContext(ctx).Save(model).Error
}

func (r *PostgresAddressMappingRepository) FindByInternalAddress(ctx context.Context, addr string) (*entity.AddressMapping, error) {
	var model addressMappingModel
	err := r.db.WithContext(ctx).Where("internal_address = ?", addr).First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresAddressMappingRepository) FindByChainAddress(ctx context.Context, chain, addr string) (*entity.AddressMapping, error) {
	var model addressMappingModel
	err := r.db.WithContext(ctx).
		Where("chain_type = ? AND chain_address = ? AND is_active = true", chain, addr).
		First(&model).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return model.toEntity(), nil
}

func (r *PostgresAddressMappingRepository) FindByUserID(ctx context.Context, userID string) ([]*entity.AddressMapping, error) {
	var models []addressMappingModel
	err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&models).Error
	if err != nil {
		return nil, err
	}
	result := make([]*entity.AddressMapping, len(models))
	for i := range models {
		result[i] = models[i].toEntity()
	}
	return result, nil
}

func (r *PostgresAddressMappingRepository) Delete(ctx context.Context, id string) error {
	return r.db.WithContext(ctx).Where("id = ?", id).Delete(&addressMappingModel{}).Error
}
