package cli

import (
	"context"
	"sort"

	"github.com/pkg/errors"

	"github.com/kopia/kopia/repo"
	"github.com/kopia/kopia/snapshot"
	"github.com/kopia/kopia/snapshot/policy"
)

type commandSnapshotExpire struct {
	snapshotExpireAll    bool
	snapshotExpirePaths  []string
	snapshotExpireDelete bool
}

func (c *commandSnapshotExpire) setup(svc appServices, parent commandParent) {
	cmd := parent.Command("expire", "Remove old snapshots according to defined expiration policies.")

	cmd.Flag("all", "Expire all snapshots").BoolVar(&c.snapshotExpireAll)
	cmd.Arg("path", "Expire snapshots for given paths only").StringsVar(&c.snapshotExpirePaths)
	cmd.Flag("delete", "Whether to actually delete snapshots").BoolVar(&c.snapshotExpireDelete)
	cmd.Action(svc.repositoryWriterAction(c.run))
}

func (c *commandSnapshotExpire) getSnapshotSourcesToExpire(ctx context.Context, rep repo.Repository) ([]snapshot.SourceInfo, error) {
	if c.snapshotExpireAll {
		// nolint:wrapcheck
		return snapshot.ListSources(ctx, rep)
	}

	var result []snapshot.SourceInfo

	for _, p := range c.snapshotExpirePaths {
		src, err := snapshot.ParseSourceInfo(p, rep.ClientOptions().Hostname, rep.ClientOptions().Username)
		if err != nil {
			return nil, errors.Wrapf(err, "unable to parse %q", p)
		}

		result = append(result, src)
	}

	return result, nil
}

func (c *commandSnapshotExpire) run(ctx context.Context, rep repo.RepositoryWriter) error {
	sources, err := c.getSnapshotSourcesToExpire(ctx, rep)
	if err != nil {
		return err
	}

	sort.Slice(sources, func(i, j int) bool {
		return sources[i].String() < sources[j].String()
	})

	for _, src := range sources {
		deleted, err := policy.ApplyRetentionPolicy(ctx, rep, src, c.snapshotExpireDelete)
		if err != nil {
			return errors.Wrapf(err, "error applying retention policy to %v", src)
		}

		if len(deleted) == 0 {
			log(ctx).Infof("Nothing to delete for %v.", src)
			continue
		}

		if c.snapshotExpireDelete {
			log(ctx).Infof("Deleted %v snapshots of %v...", len(deleted), src)
		} else {
			log(ctx).Infof("%v snapshot(s) of %v would be deleted. Pass --delete to do it.", len(deleted), src)

			for _, it := range deleted {
				log(ctx).Infof("  %v", formatTimestamp(it.StartTime))
			}
		}
	}

	return nil
}
