package service

import (
	"bytes"
	"context"
	"strings"

	otherperson "wucher/internal/domain/other_person"
)

type OtherPersonService struct{ repo otherperson.Repository }

func NewOtherPersonService(repo otherperson.Repository) *OtherPersonService {
	return &OtherPersonService{repo: repo}
}

func (s *OtherPersonService) Upsert(ctx context.Context, p *otherperson.OtherPerson) error {
	return s.repo.Upsert(ctx, p)
}

// Create registers a new master person. Name is required and the (name, mobile_phone,
// email) identity must be unique among live rows.
func (s *OtherPersonService) 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)
	if p.Name == "" {
		return otherperson.ErrNameRequired
	}
	existing, err := s.repo.FindByIdentity(ctx, p.Name, p.MobilePhone, p.Email)
	if err != nil {
		return err
	}
	if existing != nil {
		return otherperson.ErrDuplicate
	}
	return s.repo.Create(ctx, p)
}

// Update replaces the identity fields of an existing master person. The new identity must
// not collide with a different live row.
func (s *OtherPersonService) Update(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)
	if p.Name == "" {
		return otherperson.ErrNameRequired
	}
	current, err := s.repo.GetByID(ctx, p.ID)
	if err != nil {
		return err
	}
	if current == nil {
		return otherperson.ErrNotFound
	}
	existing, err := s.repo.FindByIdentity(ctx, p.Name, p.MobilePhone, p.Email)
	if err != nil {
		return err
	}
	if existing != nil && !bytes.Equal(existing.ID, p.ID) {
		return otherperson.ErrDuplicate
	}
	return s.repo.Update(ctx, p)
}

func (s *OtherPersonService) Delete(ctx context.Context, id, actor []byte) error {
	current, err := s.repo.GetByID(ctx, id)
	if err != nil {
		return err
	}
	if current == nil {
		return otherperson.ErrNotFound
	}
	return s.repo.Delete(ctx, id, actor)
}

func (s *OtherPersonService) GetByID(ctx context.Context, id []byte) (*otherperson.OtherPerson, error) {
	return s.repo.GetByID(ctx, id)
}

func (s *OtherPersonService) Search(ctx context.Context, q string, limit, offset int) ([]otherperson.OtherPerson, int64, error) {
	return s.repo.Search(ctx, q, limit, offset)
}
