package main

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"sync"
	"time"
)

type Key struct{ Tenant, Operation, ID string }
type Entry struct {
	Hash            [32]byte
	Generation      uint64
	State, Response string
	Expires         time.Time
}
type Store struct {
	mu      sync.Mutex
	entries map[Key]Entry
	next    uint64
	balance int
}

func NewStore() *Store { return &Store{entries: make(map[Key]Entry)} }

// Start and Commit model serializable changes in one process; no durable database is used.
func (s *Store) Start(k Key, payload []byte, now time.Time) (Entry, string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	h := sha256.Sum256(payload)
	if e, ok := s.entries[k]; ok {
		if e.State == "succeeded" && !now.Before(e.Expires) {
			delete(s.entries, k)
		} else {
			if e.Hash != h {
				return e, "conflict"
			}
			if e.State == "succeeded" {
				return e, "replay"
			}
			return e, e.State
		}
	}
	s.next++
	e := Entry{Hash: h, Generation: s.next, State: "pending"}
	s.entries[k] = e
	return e, "claimed"
}
func (s *Store) Commit(k Key, generation uint64, delta int, now time.Time, retention time.Duration) (string, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	e, ok := s.entries[k]
	if !ok || e.Generation != generation || e.State != "pending" {
		return "", errors.New("stale or unresolved owner")
	}
	if retention <= 0 {
		return "", errors.New("retention must be positive")
	}
	s.balance += delta
	e.State = "succeeded"
	e.Response = fmt.Sprintf("balance=%d", s.balance)
	e.Expires = now.Add(retention)
	s.entries[k] = e
	return e.Response, nil
}
func (s *Store) MarkUnknown(k Key, generation uint64) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	e, ok := s.entries[k]
	if !ok || e.Generation != generation || e.State != "pending" {
		return errors.New("stale owner")
	}
	e.State = "unknown"
	s.entries[k] = e
	return nil
}
func (s *Store) Balance() int { s.mu.Lock(); defer s.mu.Unlock(); return s.balance }
func main() {
	s := NewStore()
	k := Key{"tenant-a", "credit", "op-1"}
	now := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
	payload := []byte("amount=10")
	e, status := s.Start(k, payload, now)
	fmt.Println("first:", status)
	_, status = s.Start(k, payload, now)
	fmt.Println("concurrent duplicate:", status)
	response, err := s.Commit(k, e.Generation, 10, now, time.Hour)
	fmt.Println("commit:", response, err)
	e, status = s.Start(k, payload, now)
	fmt.Println("retry:", status, e.Response, "balance:", s.Balance())
	_, status = s.Start(k, []byte("amount=99"), now)
	fmt.Println("changed payload:", status)
	_, status = s.Start(k, payload, now.Add(time.Hour))
	fmt.Println("after retention:", status)
	k2 := Key{"tenant-a", "external-credit", "op-2"}
	e, _ = s.Start(k2, payload, now)
	_ = s.MarkUnknown(k2, e.Generation)
	_, status = s.Start(k2, payload, now.Add(24*time.Hour))
	fmt.Println("unresolved external result:", status)
}
