package server

import (
	"fmt"

	"git.mstar.dev/mstar/goutils/sliceutils"
	"git.mstar.dev/mstar/linstrom/storage"
)

func convertAccountStorageToLinstrom(
	acc *storage.Account,
	store *storage.Storage,
) (*linstromAccount, error) {
	storageServer, err := store.FindRemoteServerById(acc.ServerId)
	if err != nil {
		return nil, fmt.Errorf("remote server: %w", err)
	}
	apiServer, err := convertServerStorageToLinstrom(storageServer, store)
	if err != nil {
		return nil, fmt.Errorf("remote server conversion: %w", err)
	}
	storageIcon, err := store.GetMediaMetadataById(acc.Icon)
	if err != nil {
		return nil, fmt.Errorf("icon: %w", err)
	}
	var apiBanner *linstromMediaMetadata
	if acc.Banner != nil {
		storageBanner, err := store.GetMediaMetadataById(*acc.Banner)
		if err != nil {
			return nil, fmt.Errorf("banner: %w", err)
		}
		apiBanner = convertMediaMetadataStorageToLinstrom(storageBanner)
	}

	var apiBackground *linstromMediaMetadata
	if acc.Background != nil {
		storageBackground, err := store.GetMediaMetadataById(*acc.Background)
		if err != nil {
			return nil, fmt.Errorf("banner: %w", err)
		}
		apiBackground = convertMediaMetadataStorageToLinstrom(storageBackground)
	}
	storageFields, err := store.FindMultipleUserFieldsById(acc.CustomFields)
	if err != nil {
		return nil, fmt.Errorf("customFields: %w", err)
	}

	return &linstromAccount{
		Id:             acc.ID,
		CreatedAt:      acc.CreatedAt,
		UpdatedAt:      &acc.UpdatedAt,
		Username:       acc.Username,
		OriginServer:   apiServer,
		OriginServerId: int(acc.ServerId),
		DisplayName:    acc.DisplayName,
		CustomFields: sliceutils.Map(
			storageFields,
			func(t storage.UserInfoField) *linstromCustomAccountField {
				return convertInfoFieldStorageToLinstrom(t)
			},
		),
		CustomFieldIds:   acc.CustomFields,
		IsBot:            acc.IsBot,
		Description:      acc.Description,
		Icon:             convertMediaMetadataStorageToLinstrom(storageIcon),
		IconId:           acc.Icon,
		Banner:           apiBanner,
		BannerId:         acc.Banner,
		Background:       apiBackground,
		BackgroundId:     acc.Background,
		RelationIds:      acc.Relations,
		Indexable:        acc.Indexable,
		RestrictedFollow: acc.RestrictedFollow,
		IdentifiesAs: sliceutils.Map(
			acc.IdentifiesAs,
			func(t storage.Being) string { return string(t) },
		),
		Pronouns: acc.Gender,
		Roles:    acc.Roles,
	}, nil
}

func convertServerStorageToLinstrom(
	server *storage.RemoteServer,
	store *storage.Storage,
) (*linstromOriginServer, error) {
	storageMeta, err := store.GetMediaMetadataById(server.Icon)
	if err != nil {
		return nil, fmt.Errorf("icon metadata: %w", err)
	}
	return &linstromOriginServer{
		Id:          server.ID,
		CreatedAt:   server.CreatedAt,
		UpdatedAt:   &server.UpdatedAt,
		ServerType:  string(server.ServerType),
		Domain:      server.Domain,
		DisplayName: server.Name,
		Icon:        convertMediaMetadataStorageToLinstrom(storageMeta),
		IsSelf:      server.IsSelf,
	}, nil
}

func convertMediaMetadataStorageToLinstrom(metadata *storage.MediaMetadata) *linstromMediaMetadata {
	return &linstromMediaMetadata{
		Id:        metadata.ID,
		CreatedAt: metadata.CreatedAt,
		UpdatedAt: &metadata.UpdatedAt,
		IsRemote:  metadata.Remote,
		Url:       metadata.Location,
		MimeType:  metadata.Type,
		Name:      metadata.Name,
		AltText:   metadata.AltText,
		Blurred:   metadata.Blurred,
	}
}

func convertInfoFieldStorageToLinstrom(field storage.UserInfoField) *linstromCustomAccountField {
	return &linstromCustomAccountField{
		Id:          field.ID,
		CreatedAt:   field.CreatedAt,
		UpdatedAt:   &field.UpdatedAt,
		Key:         field.Name,
		Value:       field.Value,
		Verified:    &field.Confirmed,
		BelongsToId: field.BelongsTo,
	}
}

func convertNoteStorageToLinstrom(
	note *storage.Note,
	store *storage.Storage,
) (*linstromNote, error) {
	panic("Not implemented")
}

func convertEmoteStorageToLinstrom(
	emote *storage.Emote,
	store *storage.Storage,
) (*linstromEmote, error) {
	storageServer, err := store.FindRemoteServerById(emote.ServerId)
	if err != nil {
		return nil, fmt.Errorf("server: %w", err)
	}
	server, err := convertServerStorageToLinstrom(storageServer, store)
	if err != nil {
		return nil, fmt.Errorf("server conversion: %w", err)
	}
	storageMedia, err := store.GetMediaMetadataById(emote.MetadataId)
	if err != nil {
		return nil, fmt.Errorf("media metadata: %w", err)
	}
	media := convertMediaMetadataStorageToLinstrom(storageMedia)

	return &linstromEmote{
		Id:         emote.ID,
		MetadataId: emote.MetadataId,
		Metadata:   media,
		Name:       emote.Name,
		ServerId:   emote.ServerId,
		Server:     server,
	}, nil
}

func convertReactionStorageToLinstrom(
	reaction *storage.Reaction,
	store *storage.Storage,
) (*linstromReaction, error) {
	storageEmote, err := store.GetEmoteById(reaction.EmoteId)
	if err != nil {
		return nil, fmt.Errorf("emote: %w", err)
	}
	emote, err := convertEmoteStorageToLinstrom(storageEmote, store)
	if err != nil {
		return nil, fmt.Errorf("emote conversion: %w", err)
	}

	return &linstromReaction{
		Id:        reaction.ID,
		NoteId:    reaction.NoteId,
		ReactorId: reaction.ReactorId,
		EmoteId:   reaction.EmoteId,
		Emote:     emote,
	}, nil
}
