package media

import (
	"bytes"
	"context"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"net/textproto"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/store/attachment"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

type fakePersonalStickerStore struct {
	listErr   error
	uploadErr error
	deleteErr error
	getResult attachment.AttachmentDownload
	getErr    error
}

func (f *fakePersonalStickerStore) AuthenticateSession(_ context.Context, _ string) (identity.AuthenticatedUser, error) {
	return identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}}, nil
}

func (f *fakePersonalStickerStore) ListPersonalStickersForUser(_ context.Context, _ string, _ string) ([]attachment.PersonalSticker, error) {
	if f.listErr != nil {
		return nil, f.listErr
	}
	return nil, nil
}

func (f *fakePersonalStickerStore) UploadPersonalStickersForUser(_ context.Context, _ string, _ string, _ []attachment.CreateAttachmentParams) ([]attachment.PersonalSticker, error) {
	if f.uploadErr != nil {
		return nil, f.uploadErr
	}
	return nil, nil
}

func (f *fakePersonalStickerStore) DeletePersonalStickerForUser(_ context.Context, _ string, _ string, _ string) error {
	return f.deleteErr
}

func (f *fakePersonalStickerStore) LoadPersonalStickerForUser(_ context.Context, _ string, _ string, _ string) (attachment.AttachmentDownload, error) {
	if f.getErr != nil {
		return attachment.AttachmentDownload{}, f.getErr
	}
	return f.getResult, nil
}

func newFakePersonalStickerHandler(fake *fakePersonalStickerStore) *PersonalStickerHandler {
	return &PersonalStickerHandler{authStore: fake, store: fake}
}

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

	handler := newFakePersonalStickerHandler(&fakePersonalStickerStore{})
	router := gin.New()
	router.GET("/api/v1/workspaces/wsp_1/stickers", func(ctx *gin.Context) {
		api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
		api.HandleWorkspaceScoped(handler.List)(ctx)
	})

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/stickers", nil)
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "No workspace selected")
}

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

	handler := newFakePersonalStickerHandler(&fakePersonalStickerStore{})
	router := gin.New()
	router.POST("/api/v1/workspaces/wsp_1/stickers", handler.Upload)

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/v1/workspaces/wsp_1/stickers", nil)
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusUnauthorized, recorder.Code)
}

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

	handler := newFakePersonalStickerHandler(&fakePersonalStickerStore{})
	router := gin.New()
	router.POST("/api/v1/workspaces/wsp_1/stickers", func(ctx *gin.Context) {
		api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
		handler.Upload(ctx)
	})

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreatePart(textproto.MIMEHeader{
		"Content-Disposition": []string{`form-data; name="files"; filename="smile.png"`},
		"Content-Type":        []string{"image/png"},
	})
	require.NoError(t, err)
	_, err = part.Write([]byte("\x89PNG\r\n\x1a\n"))
	require.NoError(t, err)
	require.NoError(t, writer.Close())

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/v1/workspaces/wsp_1/stickers", body)
	request.Header.Set("Content-Type", writer.FormDataContentType())
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "No workspace selected")
}

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

	fake := &fakePersonalStickerStore{uploadErr: storeerr.ErrForbidden}
	handler := newFakePersonalStickerHandler(fake)
	router := gin.New()
	router.POST("/api/v1/workspaces/wsp_1/stickers", func(ctx *gin.Context) {
		api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
		api.SetCurrentWorkspace(ctx, workspace.Summary{WorkspaceID: "wsp_1"})
		handler.Upload(ctx)
	})

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreatePart(textproto.MIMEHeader{
		"Content-Disposition": []string{`form-data; name="files"; filename="smile.png"`},
		"Content-Type":        []string{"image/png"},
	})
	require.NoError(t, err)
	_, err = part.Write([]byte("\x89PNG\r\n\x1a\n"))
	require.NoError(t, err)
	require.NoError(t, writer.Close())

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPost, "/api/v1/workspaces/wsp_1/stickers", body)
	request.Header.Set("Content-Type", writer.FormDataContentType())
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusForbidden, recorder.Code)
}

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

	handler := newFakePersonalStickerHandler(&fakePersonalStickerStore{})
	router := gin.New()
	router.DELETE("/api/v1/workspaces/wsp_1/stickers/:stickerId", func(ctx *gin.Context) {
		api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
		api.HandleWorkspaceScoped(handler.Delete)(ctx)
	})

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodDelete, "/api/v1/workspaces/wsp_1/stickers/stk_1", nil)
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Contains(t, recorder.Body.String(), "No workspace selected")
}

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

	handler := newFakePersonalStickerHandler(&fakePersonalStickerStore{
		getResult: attachment.AttachmentDownload{
			Filename:    "smile.png",
			ContentType: "image/png",
			Blob:        []byte("png-data"),
		},
	})
	router := gin.New()
	router.GET("/api/v1/workspaces/:workspaceId/stickers/:stickerId", handler.Get)

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/stickers/stk_1", nil)
	request.Header.Set("Authorization", "Bearer user-token")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusOK, recorder.Code)
	require.Equal(t, "image/png", recorder.Header().Get("Content-Type"))
	require.Equal(t, "png-data", recorder.Body.String())
}
