package mysql

import (
	"context"
	"errors"
	"strings"
	"time"

	"gorm.io/gorm"

	otherperson "wucher/internal/domain/other_person"
)

type OtherPersonRepository struct{ db *gorm.DB }

func NewOtherPersonRepository(db *gorm.DB) *OtherPersonRepository {
	return &OtherPersonRepository{db: db}
}

func (r *OtherPersonRepository) Create(ctx context.Context, p *otherperson.OtherPerson) error {
	p.Name = strings.TrimSpace(p.Name)
	p.MobilePhone = strings.TrimSpace(p.MobilePhone)
	p.Email = strings.TrimSpace(p.Email)
	return r.db.WithContext(ctx).Create(p).Error
}

func (r *OtherPersonRepository) Update(ctx context.Context, p *otherperson.OtherPerson) error {
	return r.db.WithContext(ctx).
		Model(&otherperson.OtherPerson{}).
		Where("id = ? AND deleted_at IS NULL", p.ID).
		Updates(map[string]any{
			"name":         strings.TrimSpace(p.Name),
			"mobile_phone": strings.TrimSpace(p.MobilePhone),
			"email":        strings.TrimSpace(p.Email),
			"updated_by":   p.UpdatedBy,
		}).Error
}

func (r *OtherPersonRepository) Delete(ctx context.Context, id, deletedBy []byte) error {
	return r.db.WithContext(ctx).
		Model(&otherperson.OtherPerson{}).
		Where("id = ? AND deleted_at IS NULL", id).
		Updates(map[string]any{
			"deleted_at": time.Now().UTC(),
			"deleted_by": deletedBy,
		}).Error
}

func (r *OtherPersonRepository) FindByIdentity(ctx context.Context, name, phone, email string) (*otherperson.OtherPerson, error) {
	var row otherperson.OtherPerson
	err := r.db.WithContext(ctx).
		Where("name = ? AND mobile_phone = ? AND email = ? AND deleted_at IS NULL",
			strings.TrimSpace(name), strings.TrimSpace(phone), strings.TrimSpace(email)).
		First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &row, err
}

func (r *OtherPersonRepository) Upsert(ctx context.Context, p *otherperson.OtherPerson) error {
	name := strings.TrimSpace(p.Name)
	phone := strings.TrimSpace(p.MobilePhone)
	email := strings.TrimSpace(p.Email)

	var existing otherperson.OtherPerson
	err := r.db.WithContext(ctx).
		Where("name = ? AND mobile_phone = ? AND email = ? AND deleted_at IS NULL", name, phone, email).
		First(&existing).Error
	if err == nil {
		p.ID = existing.ID
		p.Name, p.MobilePhone, p.Email = existing.Name, existing.MobilePhone, existing.Email
		return nil
	}
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}
	p.Name, p.MobilePhone, p.Email = name, phone, email
	return r.db.WithContext(ctx).Create(p).Error
}

func (r *OtherPersonRepository) GetByID(ctx context.Context, id []byte) (*otherperson.OtherPerson, error) {
	var row otherperson.OtherPerson
	err := r.db.WithContext(ctx).
		Where("id = ? AND deleted_at IS NULL", id).
		First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &row, err
}

func (r *OtherPersonRepository) Search(ctx context.Context, q string, limit, offset int) ([]otherperson.OtherPerson, int64, error) {
	rows := make([]otherperson.OtherPerson, 0)
	base := r.db.WithContext(ctx).
		Model(&otherperson.OtherPerson{}).
		Where("deleted_at IS NULL")
	if s := strings.TrimSpace(q); s != "" {
		like := "%" + s + "%"
		base = base.Where("name LIKE ? OR mobile_phone LIKE ? OR email LIKE ?", like, like, like)
	}
	var total int64
	if err := base.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if limit <= 0 {
		limit = 20
	}
	err := base.Order("created_at DESC").
		Limit(limit).
		Offset(offset).
		Find(&rows).Error
	return rows, total, err
}
