package attachment

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/objectstorage"
)

type fakeMediaStorage struct {
	driver   string
	getBlob  []byte
	getErr   error
	fetched  []string
	deleted  []string
	uploaded []string
}

func (fake *fakeMediaStorage) Driver() string { return fake.driver }

func (fake *fakeMediaStorage) Put(_ context.Context, key string, _ string, _ []byte) error {
	fake.uploaded = append(fake.uploaded, key)
	return nil
}

func (fake *fakeMediaStorage) Get(_ context.Context, key string) ([]byte, error) {
	fake.fetched = append(fake.fetched, key)
	if fake.getErr != nil {
		return nil, fake.getErr
	}
	return append([]byte(nil), fake.getBlob...), nil
}

func (fake *fakeMediaStorage) Delete(_ context.Context, key string) error {
	fake.deleted = append(fake.deleted, key)
	return nil
}

func TestLoadMediaObjectFallsBackToBlobWhenStorageDisabled(t *testing.T) {
	t.Parallel()

	store := &Store{}
	blob, err := store.loadMediaObject(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1", []byte("db"))
	require.NoError(t, err)
	require.Equal(t, []byte("db"), blob)
}

func TestLoadMediaObjectUsesObjectStoragePayload(t *testing.T) {
	t.Parallel()

	store := &Store{sharedStore: sharedStore{mediaObjectStorage: &fakeMediaStorage{driver: objectstorage.DriverOSS, getBlob: []byte("oss")}}}
	blob, err := store.loadMediaObject(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1", []byte("db"))
	require.NoError(t, err)
	require.Equal(t, []byte("oss"), blob)
}

func TestLoadMediaObjectFallsBackWhenHistoricalBlobStillExists(t *testing.T) {
	t.Parallel()

	store := &Store{sharedStore: sharedStore{mediaObjectStorage: &fakeMediaStorage{driver: objectstorage.DriverOSS, getErr: objectstorage.ErrObjectNotFound}}}
	blob, err := store.loadMediaObject(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1", []byte("db"))
	require.NoError(t, err)
	require.Equal(t, []byte("db"), blob)
}

func TestLoadMediaObjectReturnsErrorWhenObjectMissingWithoutFallback(t *testing.T) {
	t.Parallel()

	store := &Store{sharedStore: sharedStore{mediaObjectStorage: &fakeMediaStorage{driver: objectstorage.DriverOSS, getErr: objectstorage.ErrObjectNotFound}}}
	_, err := store.loadMediaObject(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1", nil)
	require.Error(t, err)
	require.Contains(t, err.Error(), "attachments/wsp-1/att-1")
}

func TestDeleteMediaObjectBestEffortSkipsEmptyTarget(t *testing.T) {
	t.Parallel()

	fake := &fakeMediaStorage{driver: objectstorage.DriverOSS}
	store := &Store{sharedStore: sharedStore{mediaObjectStorage: fake}}
	store.deleteMediaObjectBestEffort(context.Background(), objectstorage.DriverOSS, "")
	require.Empty(t, fake.deleted)
}

func TestDeleteMediaObjectBestEffortDeletesWhenConfigured(t *testing.T) {
	t.Parallel()

	fake := &fakeMediaStorage{driver: objectstorage.DriverOSS}
	store := &Store{sharedStore: sharedStore{mediaObjectStorage: fake}}
	store.deleteMediaObjectBestEffort(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1")
	require.Equal(t, []string{"attachments/wsp-1/att-1"}, fake.deleted)
}

func TestPutMediaObjectUsesTypedObjectKey(t *testing.T) {
	t.Parallel()

	fake := &fakeMediaStorage{driver: objectstorage.DriverOSS}
	store := &Store{sharedStore: sharedStore{mediaObjectStorage: fake}}
	driver, objectKey, err := store.putMediaObject(context.Background(), mediaObjectTypeAttachment, "wsp-1", "att-1", "image/png", []byte("blob"))
	require.NoError(t, err)
	require.Equal(t, objectstorage.DriverOSS, driver)
	require.Equal(t, "attachments/wsp-1/att-1", objectKey)
	require.Equal(t, []string{"attachments/wsp-1/att-1"}, fake.uploaded)
}

func TestLoadMediaObjectPropagatesUnexpectedError(t *testing.T) {
	t.Parallel()

	store := &Store{sharedStore: sharedStore{mediaObjectStorage: &fakeMediaStorage{driver: objectstorage.DriverOSS, getErr: errors.New("boom")}}}
	_, err := store.loadMediaObject(context.Background(), objectstorage.DriverOSS, "attachments/wsp-1/att-1", nil)
	require.Error(t, err)
	require.Contains(t, err.Error(), "boom")
}

func TestMediaObjectPublicURLBuildsExpectedCDNPath(t *testing.T) {
	t.Parallel()

	store := &Store{
		sharedStore: sharedStore{
			mediaPublicBaseURL: "https://media.agentteam.pingcap.com",
			mediaObjectPrefix:  "agentteam/objects",
		},
	}
	publicURL := store.mediaObjectPublicURL(objectstorage.DriverOSS, "attachments/wsp-1/att-1")
	require.Equal(t, "https://media.agentteam.pingcap.com/agentteam/objects/attachments/wsp-1/att-1", publicURL)
}

func TestMediaObjectPublicURLSkipsWhenBaseURLMissing(t *testing.T) {
	t.Parallel()

	store := &Store{}
	require.Empty(t, store.mediaObjectPublicURL(objectstorage.DriverOSS, "attachments/wsp-1/att-1"))
}
