package worker

import (
	"context"
	"fmt"
	"path/filepath"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/tidbcloud/observability/backup-worker/pkg/configs"
	"github.com/tidbcloud/observability/backup-worker/pkg/syncer"
	"github.com/tidbcloud/observability/backup-worker/pkg/worker/checkpoint"
)

type FileWorker struct {
	cfg    *configs.Config
	cp     *checkpoint.FileCheckpoint
	shards []configs.FileShard
	repl   syncer.Syncer
}

func NewFileWorker(ctx context.Context, cfg *configs.Config) (*FileWorker, error) {
	repl, err := syncer.New(ctx, *cfg)
	if err != nil {
		return nil, fmt.Errorf("create syncer: %w", err)
	}

	shards := cfg.Syncers.Shards(cfg.Mode)
	return &FileWorker{
		cfg:    cfg,
		cp:     checkpoint.NewFileCheckpoint(filepath.Join(cfg.Checkpoint.Path, cfg.Mode, cfg.ID()), len(shards)),
		shards: shards,
		repl:   repl,
	}, nil
}

func (w *FileWorker) Close() error {
	return w.repl.Close()
}

func (w *FileWorker) Run(ctx context.Context) error {
	if err := w.cp.Load(); err != nil {
		return err
	}

	logrus.Infof("Starting replication: type=%s, shards=%d, concurrency=%d",
		w.repl.Type(), len(w.shards), w.cfg.Execution.Concurrency)

	return runWorker(ctx, w.shards, w.cfg.Execution.Concurrency,
		func(i int, shard configs.FileShard) bool {
			var skip bool
			w.cp.AccessState(func(state *checkpoint.FileState) {
				skip = state.Shards[i].Status == checkpoint.StatusCompleted
			})
			return skip
		},
		w.process,
	)
}

func (w *FileWorker) process(ctx context.Context, shard configs.FileShard) error {
	idx := shard.Index

	logrus.Infof("Start process shard: %+v", shard)
	w.cp.UpdateState(func(state *checkpoint.FileState) {
		state.Shards[idx].Status = checkpoint.StatusRunning
		state.Shards[idx].StartedAt = time.Now().Unix()
	})

	fileSize, err := w.repl.Sync(ctx, shard)
	if err != nil {
		w.cp.UpdateShardFailed(idx)
		return err
	}

	w.cp.UpdateState(func(state *checkpoint.FileState) {
		state.Shards[idx].Status = checkpoint.StatusCompleted
		state.Shards[idx].FileSize = fileSize
		state.Shards[idx].CompletedAt = time.Now().Unix()
	})

	if fileSize > 0 {
		logrus.Infof("FileShard completed: %+v", shard)
	} else {
		logrus.Infof("No-ops FileShard completed: %+v", shard)
	}
	return nil
}
