package repo

import (
	"errors"
	"fmt"
	"strings"

	"trustcontact/internal/models"

	"gorm.io/gorm"
)

type UserRepo struct {
	db *gorm.DB
}

type UserListParams struct {
	Query    string
	Sort     string
	Dir      string
	Page     int
	PageSize int
}

func NewUserRepo(db *gorm.DB) *UserRepo {
	return &UserRepo{db: db}
}

func (r *UserRepo) FindByID(id string) (*models.User, error) {
	var user models.User
	if err := r.db.Preload("Properties").Where("id = ?", id).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}

	return &user, nil
}

func (r *UserRepo) FindByEmail(email string) (*models.User, error) {
	var user models.User
	if err := r.db.Preload("Properties").Where("email = ?", email).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}

	return &user, nil
}

func (r *UserRepo) Create(user *models.User) error {
	return r.db.Create(user).Error
}

func (r *UserRepo) SetEmailVerified(userID string, verified bool) error {
	return r.db.Model(&models.User{}).
		Where("id = ?", userID).
		Update("email_verified", verified).Error
}

func (r *UserRepo) UpdatePasswordHash(userID string, passwordHash string) error {
	return r.db.Model(&models.User{}).
		Where("id = ?", userID).
		Update("password_hash", passwordHash).Error
}

func (r *UserRepo) UpsertLanguagePreference(userID string, lang string) error {
	var existing models.UserProperties
	err := r.db.Where("user_id = ?", userID).First(&existing).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}

	if errors.Is(err, gorm.ErrRecordNotFound) {
		props := models.UserProperties{
			UserId: userID,
			Lang:   lang,
		}
		return r.db.Create(&props).Error
	}

	return r.db.Model(&models.UserProperties{}).
		Where("user_id = ?", userID).
		Update("lang", lang).Error
}

func (r *UserRepo) UpsertDarkPreference(userID string, dark bool) error {
	var existing models.UserProperties
	err := r.db.Where("user_id = ?", userID).First(&existing).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}

	if errors.Is(err, gorm.ErrRecordNotFound) {
		props := models.UserProperties{
			UserId: userID,
			Dark:   dark,
		}
		return r.db.Create(&props).Error
	}

	return r.db.Model(&models.UserProperties{}).
		Where("user_id = ?", userID).
		Update("dark", dark).Error
}

func (r *UserRepo) List(params UserListParams) ([]models.User, int64, error) {
	query := r.db.Model(&models.User{})

	search := strings.TrimSpace(params.Query)
	if search != "" {
		like := "%" + strings.ToLower(search) + "%"
		query = query.Where("LOWER(name) LIKE ? OR LOWER(email) LIKE ?", like, like)
	}

	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	orderBy := sanitizeSort(params.Sort)
	orderDir := sanitizeDir(params.Dir)
	orderClause := fmt.Sprintf("%s %s", orderBy, orderDir)

	page := params.Page
	if page < 1 {
		page = 1
	}
	pageSize := params.PageSize
	if pageSize <= 0 {
		pageSize = 10
	}
	if pageSize > 500 {
		pageSize = 500
	}

	offset := (page - 1) * pageSize

	var users []models.User
	if err := query.Order(orderClause).Limit(pageSize).Offset(offset).Find(&users).Error; err != nil {
		return nil, 0, err
	}

	return users, total, nil
}

func sanitizeSort(sort string) string {
	switch strings.ToLower(strings.TrimSpace(sort)) {
	case "id":
		return "id"
	case "name":
		return "name"
	case "email":
		return "email"
	default:
		return "id"
	}
}

func sanitizeDir(dir string) string {
	switch strings.ToLower(strings.TrimSpace(dir)) {
	case "desc":
		return "desc"
	default:
		return "asc"
	}
}
