package sharepoint

import (
	"context"

	"github.com/alcionai/clues"

	"github.com/alcionai/corso/src/internal/common/prefixmatcher"
	"github.com/alcionai/corso/src/internal/data"
	"github.com/alcionai/corso/src/internal/m365/collection/drive"
	"github.com/alcionai/corso/src/internal/m365/collection/site"
	"github.com/alcionai/corso/src/internal/m365/support"
	"github.com/alcionai/corso/src/internal/operations/inject"
	"github.com/alcionai/corso/src/pkg/account"
	"github.com/alcionai/corso/src/pkg/backup/metadata"
	"github.com/alcionai/corso/src/pkg/count"
	"github.com/alcionai/corso/src/pkg/fault"
	"github.com/alcionai/corso/src/pkg/path"
	"github.com/alcionai/corso/src/pkg/services/m365/api"
	"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
)

type sharePointBackup struct{}

// NewBackup provides a struct that matches standard apis
// across m365/service handlers.
func NewBackup() *sharePointBackup {
	return &sharePointBackup{}
}

func (sharePointBackup) ProduceBackupCollections(
	ctx context.Context,
	bpc inject.BackupProducerConfig,
	ac api.Client,
	creds account.M365Config,
	su support.StatusUpdater,
	counter *count.Bus,
	errs *fault.Bus,
) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, bool, error) {
	b, err := bpc.Selector.ToSharePointBackup()
	if err != nil {
		return nil, nil, false, clues.Wrap(err, "sharePointDataCollection: parsing selector")
	}

	var (
		el                   = errs.Local()
		collections          = []data.BackupCollection{}
		categories           = map[path.CategoryType]struct{}{}
		ssmb                 = prefixmatcher.NewStringSetBuilder()
		canUsePreviousBackup bool
	)

	ctx = clues.Add(
		ctx,
		"site_id", clues.Hide(bpc.ProtectedResource.ID()),
		"site_url", clues.Hide(bpc.ProtectedResource.Name()))

	for _, scope := range b.Scopes() {
		if el.Failure() != nil {
			break
		}

		var spcs []data.BackupCollection

		switch scope.Category().PathType() {
		case path.ListsCategory:
			bh := site.NewListsBackupHandler(bpc.ProtectedResource.ID(), ac.Lists())

			spcs, canUsePreviousBackup, err = site.CollectLists(
				ctx,
				bh,
				bpc,
				ac,
				creds.AzureTenantID,
				scope,
				su,
				counter,
				errs)
			if err != nil {
				el.AddRecoverable(ctx, err)
				continue
			}

		case path.LibrariesCategory:
			spcs, canUsePreviousBackup, err = site.CollectLibraries(
				ctx,
				bpc,
				drive.NewSiteBackupHandler(
					ac.Drives(),
					bpc.ProtectedResource.ID(),
					scope,
					bpc.Selector.PathService()),
				creds.AzureTenantID,
				ssmb,
				su,
				counter,
				errs)
			if err != nil {
				el.AddRecoverable(ctx, err)
				continue
			}

		case path.PagesCategory:
			spcs, err = site.CollectPages(
				ctx,
				bpc,
				creds,
				ac,
				scope,
				su,
				counter,
				errs)
			if err != nil {
				el.AddRecoverable(ctx, err)
				continue
			}

			// Lists don't make use of previous metadata
			// TODO: Revisit when we add support of pages
			canUsePreviousBackup = true
		}

		collections = append(collections, spcs...)

		categories[scope.Category().PathType()] = struct{}{}
	}

	if len(collections) > 0 {
		baseCols, err := graph.BaseCollections(
			ctx,
			collections,
			creds.AzureTenantID,
			bpc.ProtectedResource.ID(),
			path.SharePointService,
			categories,
			su,
			counter,
			errs)
		if err != nil {
			return nil, nil, false, err
		}

		collections = append(collections, baseCols...)
	}

	return collections, ssmb.ToReader(), canUsePreviousBackup, el.Failure()
}

// ListsMetadataFileNames only contains PreviousPathFileName
// and not DeltaURLsFileName because graph apis do not have delta support
// for Sharepoint Lists
func ListsMetadataFileNames() []string {
	return []string{metadata.PreviousPathFileName}
}
