package details

import (
	"time"

	"github.com/alcionai/clues"

	"github.com/alcionai/corso/src/pkg/path"
)

type ItemType int

// ItemTypes are enumerated by service (hundredth digit) and data type (ones digit).
// Ex: exchange is 00x where x is the data type.  Sharepoint is 10x, and etc.
// Every item info struct should get its own hundredth enumeration entry.
// Every item category for that service should get its own entry (even if differences
// between types aren't apparent on initial implementation, this future-proofs
// against breaking changes).
// Entries should not be rearranged.
// Additionally, any itemType directly assigned a number should not be altered.
// This applies to  OneDriveItem and FolderItem
const (
	UnknownType ItemType = 0

	// Exchange (00x)
	ExchangeContact ItemType = 1
	ExchangeEvent   ItemType = 2
	ExchangeMail    ItemType = 3

	// SharePoint (10x)
	SharePointLibrary ItemType = 101 // also used for groups
	SharePointList    ItemType = 102
	SharePointPage    ItemType = 103

	// OneDrive (20x)
	OneDriveItem ItemType = 205

	// Folder Management(30x)
	FolderItem ItemType = 306

	// Groups/Teams(40x)
	GroupsChannelMessage   ItemType = 401
	GroupsConversationPost ItemType = 402

	// Teams Chat
	TeamsChat ItemType = 501
)

func UpdateItem(item *ItemInfo, newLocPath *path.Builder) {
	// Only OneDrive and SharePoint have information about parent folders
	// contained in them.
	// Can't switch based on infoType because that's been unstable.
	if item.Exchange != nil {
		item.Exchange.UpdateParentPath(newLocPath)
	} else if item.SharePoint != nil {
		// SharePoint used to store library items with the OneDriveItem ItemType.
		// Start switching them over as we see them since there's no point in
		// keeping the old format.
		if item.SharePoint.ItemType == OneDriveItem {
			item.SharePoint.ItemType = SharePointLibrary
		}

		item.SharePoint.UpdateParentPath(newLocPath)
	} else if item.OneDrive != nil {
		item.OneDrive.UpdateParentPath(newLocPath)
	} else if item.Groups != nil {
		item.Groups.UpdateParentPath(newLocPath)
	}
}

// ItemInfo is a oneOf that contains service specific
// information about the item it tracks
type ItemInfo struct {
	Folder     *FolderInfo     `json:"folder,omitempty"`
	Exchange   *ExchangeInfo   `json:"exchange,omitempty"`
	SharePoint *SharePointInfo `json:"sharePoint,omitempty"`
	OneDrive   *OneDriveInfo   `json:"oneDrive,omitempty"`
	Groups     *GroupsInfo     `json:"groups,omitempty"`
	TeamsChats *TeamsChatsInfo `json:"teamsChats,omitempty"`
	// Optional item extension data
	Extension *ExtensionData `json:"extension,omitempty"`
}

// typedInfo should get embedded in each sesrvice type to track
// the type of item it stores for multi-item service support.

// infoType provides internal categorization for collecting like-typed ItemInfos.
// It should return the most granular value type (ex: "event" for an exchange
// calendar event).
func (i ItemInfo) infoType() ItemType {
	switch {
	case i.Folder != nil:
		return i.Folder.ItemType

	case i.Exchange != nil:
		return i.Exchange.ItemType

	case i.SharePoint != nil:
		return i.SharePoint.ItemType

	case i.OneDrive != nil:
		return i.OneDrive.ItemType

	case i.Groups != nil:
		return i.Groups.ItemType

	case i.TeamsChats != nil:
		return i.TeamsChats.ItemType
	}

	return UnknownType
}

func (i ItemInfo) size() int64 {
	switch {
	case i.Exchange != nil:
		return i.Exchange.Size

	case i.OneDrive != nil:
		return i.OneDrive.Size

	case i.SharePoint != nil:
		return i.SharePoint.Size

	case i.Groups != nil:
		return i.Groups.Size

	case i.Folder != nil:
		return i.Folder.Size

	case i.TeamsChats != nil:
		return int64(i.TeamsChats.Chat.MessageCount)
	}

	return 0
}

func (i ItemInfo) Modified() time.Time {
	switch {
	case i.Exchange != nil:
		return i.Exchange.Modified

	case i.OneDrive != nil:
		return i.OneDrive.Modified

	case i.SharePoint != nil:
		return i.SharePoint.Modified

	case i.Groups != nil:
		return i.Groups.Modified

	case i.Folder != nil:
		return i.Folder.Modified

	case i.TeamsChats != nil:
		return i.TeamsChats.Modified
	}

	return time.Time{}
}

func (i ItemInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, error) {
	switch {
	case i.Exchange != nil:
		return i.Exchange.uniqueLocation(baseLoc)

	case i.OneDrive != nil:
		return i.OneDrive.uniqueLocation(baseLoc)

	case i.SharePoint != nil:
		return i.SharePoint.uniqueLocation(baseLoc)

	case i.Groups != nil:
		return i.Groups.uniqueLocation(baseLoc)

	case i.TeamsChats != nil:
		return i.TeamsChats.uniqueLocation(baseLoc)

	default:
		return nil, clues.New("unsupported type")
	}
}

func (i ItemInfo) updateFolder(f *FolderInfo) error {
	switch {
	case i.Exchange != nil:
		return i.Exchange.updateFolder(f)

	case i.OneDrive != nil:
		return i.OneDrive.updateFolder(f)

	case i.SharePoint != nil:
		return i.SharePoint.updateFolder(f)

	case i.Groups != nil:
		return i.Groups.updateFolder(f)

	case i.TeamsChats != nil:
		return i.TeamsChats.updateFolder(f)

	default:
		return clues.New("unsupported type")
	}
}

// true if the info represents an item backed by the drive api.
func (i ItemInfo) isDriveItem() bool {
	iit := i.infoType()

	if !(iit == OneDriveItem || iit == SharePointLibrary) {
		return false
	}

	return !(i.OneDrive == nil && i.SharePoint == nil && i.Groups == nil)
}
