package attachment

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/objectstorage"
)

// mediaObjectTypeAttachmentDerivative is the object-storage type prefix for
// attachment derivatives (thumbnails). Objects are keyed deterministically —
// "attachment-derivatives/{workspaceId}/{attachmentId}/{variant}.jpg" — so no
// DB row is needed: the existence of the object IS the cache, and the variant
// rules (variant name, dimensions, encoding) live as constants in thumbnail.go.
const mediaObjectTypeAttachmentDerivative = "attachment-derivatives"

// SupportedThumbnailVersions is the code-maintained set of thumbnail specs.
// Adding a version means a new spec + a new key suffix; the client opts in via
// ?v=N. No schema migration. Superseded versions stay listed so the GC path
// deletes their stale objects.
var SupportedThumbnailVersions = []string{ThumbnailVariant, "thumb-v1"}

// AttachmentVariant is a generated derivative (thumbnail) served to clients.
type AttachmentVariant struct {
	ContentType string
	Blob        []byte
	Width       int
	Height      int
}

func derivativeObjectKey(workspaceID string, attachmentID string, variant string) string {
	return mediaObjectKey(
		mediaObjectTypeAttachmentDerivative,
		workspaceID,
		derivativeObjectID(attachmentID, variant),
	)
}

func derivativeObjectID(attachmentID string, variant string) string {
	return strings.TrimSpace(attachmentID) + "/" + strings.TrimSpace(variant) + ".jpg"
}

// LoadAttachmentThumbnail returns a cached thumbnail from object storage, or
// attachment.ErrNotFound when none has been generated yet (or object storage is
// disabled — in which case the endpoint regenerates on each request).
func (store *Store) LoadAttachmentThumbnail(ctx context.Context, workspaceID string, attachmentID string) (AttachmentVariant, error) {
	if store.mediaObjectStorage == nil {
		return AttachmentVariant{}, ErrNotFound
	}
	blob, err := store.mediaObjectStorage.Get(ctx, derivativeObjectKey(workspaceID, attachmentID, ThumbnailVariant))
	if err != nil {
		if errors.Is(err, objectstorage.ErrObjectNotFound) {
			return AttachmentVariant{}, ErrNotFound
		}
		return AttachmentVariant{}, fmt.Errorf("load attachment thumbnail: %w", err)
	}
	return AttachmentVariant{ContentType: "image/jpeg", Blob: blob}, nil
}

// SaveAttachmentThumbnail persists a generated thumbnail to object storage so
// later requests hit the cache. When object storage is disabled the bytes are
// returned without persisting (caller serves once; next request regenerates).
func (store *Store) SaveAttachmentThumbnail(ctx context.Context, workspaceID string, attachmentID string, gen GeneratedThumbnail) (AttachmentVariant, error) {
	if store.mediaObjectStorage == nil {
		return AttachmentVariant{ContentType: gen.ContentType, Blob: gen.Blob, Width: gen.Width, Height: gen.Height}, nil
	}
	objectKey := derivativeObjectKey(workspaceID, attachmentID, ThumbnailVariant)
	if err := store.mediaObjectStorage.Put(ctx, objectKey, gen.ContentType, gen.Blob); err != nil {
		return AttachmentVariant{}, fmt.Errorf("store attachment thumbnail object: %w", err)
	}
	return AttachmentVariant{ContentType: gen.ContentType, Blob: gen.Blob, Width: gen.Width, Height: gen.Height}, nil
}

// DeleteAttachmentThumbnail best-effort removes every supported thumbnail
// version for an attachment. Called from the attachment GC path so deleting an
// attachment cleans its derivatives.
func (store *Store) DeleteAttachmentThumbnail(ctx context.Context, workspaceID string, attachmentID string) {
	for _, variant := range SupportedThumbnailVersions {
		store.deleteMediaObjectBestEffort(ctx, store.mediaStorageDriver(), derivativeObjectKey(workspaceID, attachmentID, variant))
	}
}
