package mail

import (
	"context"

	"github.com/google/uuid"
)

// AdminListDeliveriesPage bundles the pagination metadata returned to
// the admin API. The same shape is reused by AdminListDeadLettersPage
// — keeping it explicit clarifies the wire contract for handlers.
type AdminListDeliveriesPage struct {
	Items    []Delivery
	Page     int
	PageSize int
	Total    int64
}

// AdminListDeadLettersPage mirrors AdminListDeliveriesPage for the
// dead-letter listing.
type AdminListDeadLettersPage struct {
	Items    []DeadLetter
	Page     int
	PageSize int
	Total    int64
}

// AdminListDeliveries returns the requested delivery page. page is
// 1-indexed; pageSize is bounded by the caller (handler defaults).
func (s *Service) AdminListDeliveries(ctx context.Context, page, pageSize int) (AdminListDeliveriesPage, error) {
	page, pageSize = normalisePaging(page, pageSize)
	offset := (page - 1) * pageSize
	items, total, err := s.deps.Store.ListDeliveries(ctx, offset, pageSize)
	if err != nil {
		return AdminListDeliveriesPage{}, err
	}
	return AdminListDeliveriesPage{
		Items:    items,
		Page:     page,
		PageSize: pageSize,
		Total:    total,
	}, nil
}

// AdminGetDelivery returns the delivery row by id, or
// ErrDeliveryNotFound when the row does not exist.
func (s *Service) AdminGetDelivery(ctx context.Context, deliveryID uuid.UUID) (Delivery, error) {
	return s.deps.Store.GetDelivery(ctx, deliveryID)
}

// AdminListAttempts returns every attempt for the delivery in
// attempt_no order. ErrDeliveryNotFound is returned when the delivery
// row itself does not exist; an empty list (no rows yet) returns nil
// without error.
func (s *Service) AdminListAttempts(ctx context.Context, deliveryID uuid.UUID) ([]Attempt, error) {
	if _, err := s.deps.Store.GetDelivery(ctx, deliveryID); err != nil {
		return nil, err
	}
	return s.deps.Store.ListAttempts(ctx, deliveryID)
}

// AdminResendDelivery re-arms the targeted row for another delivery
// cycle. The contract: ErrDeliveryNotFound when the row is missing,
// ErrResendOnSent when the row is in the terminal `sent` state.
// Otherwise the row is reset to status='pending' with attempts=0 and
// next_attempt_at=now(); the worker picks it up on the next tick.
func (s *Service) AdminResendDelivery(ctx context.Context, deliveryID uuid.UUID) (Delivery, error) {
	return s.deps.Store.ResendNonSent(ctx, deliveryID, s.deps.Now())
}

// AdminListDeadLetters returns the dead-letter page newest-first.
func (s *Service) AdminListDeadLetters(ctx context.Context, page, pageSize int) (AdminListDeadLettersPage, error) {
	page, pageSize = normalisePaging(page, pageSize)
	offset := (page - 1) * pageSize
	items, total, err := s.deps.Store.ListDeadLetters(ctx, offset, pageSize)
	if err != nil {
		return AdminListDeadLettersPage{}, err
	}
	return AdminListDeadLettersPage{
		Items:    items,
		Page:     page,
		PageSize: pageSize,
		Total:    total,
	}, nil
}

// normalisePaging clamps page and pageSize to the values handlers can
// safely pass through to the store. The defaults match what the
// existing admin endpoints use elsewhere in `internal/server`.
func normalisePaging(page, pageSize int) (int, int) {
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 25
	}
	if pageSize > 200 {
		pageSize = 200
	}
	return page, pageSize
}
