package main

import (
	"context"
	"strings"
	"time"
)

// Clock memisahkan sumber waktu dari logika bisnis. Produksi memakai
// RealClock; demo dan unit test memakai FixedClock sehingga nilai
// created_at selalu deterministik.
type Clock interface {
	Now() time.Time
}

// RealClock mengembalikan waktu sistem dalam zona UTC.
type RealClock struct{}

// Now memenuhi interface Clock.
func (RealClock) Now() time.Time { return time.Now().UTC() }

// FixedClock selalu mengembalikan Waktu yang sama.
type FixedClock struct {
	Waktu time.Time
}

// Now memenuhi interface Clock.
func (c FixedClock) Now() time.Time { return c.Waktu }

// ItemService menampung aturan bisnis item: validasi masukan,
// normalisasi nama, penanda waktu, dan pengambilan item lama. Semua
// akses penyimpanan didelegasikan ke ItemRepository yang diinjeksi.
type ItemService struct {
	repo  ItemRepository
	clock Clock
}

// NewItemService membangun service dengan repository dan clock yang
// diinjeksi pemanggil (dependency injection).
func NewItemService(repo ItemRepository, clock Clock) *ItemService {
	return &ItemService{repo: repo, clock: clock}
}

// Create memvalidasi masukan, menormalkan nama, mengisi created_at
// dari clock, lalu menyimpan item baru.
func (s *ItemService) Create(ctx context.Context, input ItemInput) (Item, error) {
	if err := input.Validasi(); err != nil {
		return Item{}, err
	}
	item := Item{
		Name:      strings.TrimSpace(input.Name),
		Price:     input.Price,
		CreatedAt: s.clock.Now(),
	}
	return s.repo.Create(ctx, item)
}

// GetByID mengambil satu item berdasarkan ID-nya.
func (s *ItemService) GetByID(ctx context.Context, id int64) (Item, error) {
	return s.repo.GetByID(ctx, id)
}

// List mengembalikan seluruh item terurut naik berdasarkan ID.
func (s *ItemService) List(ctx context.Context) ([]Item, error) {
	return s.repo.List(ctx)
}

// Update mengganti seluruh field item yang sudah ada. Item lama dibaca
// lebih dulu supaya created_at tidak berubah ketika name dan price
// diganti.
func (s *ItemService) Update(ctx context.Context, id int64, input ItemInput) (Item, error) {
	if err := input.Validasi(); err != nil {
		return Item{}, err
	}
	lama, err := s.repo.GetByID(ctx, id)
	if err != nil {
		return Item{}, err
	}
	item := Item{
		ID:        lama.ID,
		Name:      strings.TrimSpace(input.Name),
		Price:     input.Price,
		CreatedAt: lama.CreatedAt,
	}
	return s.repo.Update(ctx, item)
}

// Delete menghapus item berdasarkan ID-nya.
func (s *ItemService) Delete(ctx context.Context, id int64) error {
	return s.repo.Delete(ctx, id)
}
