package migrator

import (
	"context"
	"fmt"

	"github.com/cloudreve/Cloudreve/v4/application/migrator/model"
	"github.com/cloudreve/Cloudreve/v4/ent/file"
	"github.com/cloudreve/Cloudreve/v4/pkg/conf"
)

func (m *Migrator) migrateShare() error {
	m.l.Info("Migrating shares...")
	batchSize := 1000
	offset := m.state.ShareOffset
	ctx := context.Background()

	if offset > 0 {
		m.l.Info("Resuming share migration from offset %d", offset)
	}

	for {
		m.l.Info("Migrating shares with offset %d", offset)
		var shares []model.Share
		if err := model.DB.Limit(batchSize).Offset(offset).Find(&shares).Error; err != nil {
			return fmt.Errorf("failed to list v3 shares: %w", err)
		}

		if len(shares) == 0 {
			if m.dep.ConfigProvider().Database().Type == conf.PostgresDB {
				m.l.Info("Resetting share ID sequence for postgres...")
				m.v4client.Share.ExecContext(ctx, "SELECT SETVAL('shares_id_seq',  (SELECT MAX(id) FROM shares))")
			}
			break
		}

		tx, err := m.v4client.Tx(ctx)
		if err != nil {
			_ = tx.Rollback()
			return fmt.Errorf("failed to start transaction: %w", err)
		}

		for _, s := range shares {
			sourceId := int(s.SourceID)
			if !s.IsDir {
				sourceId += m.state.LastFolderID
			}

			// check if file exists
			_, err = tx.File.Query().Where(file.ID(sourceId)).First(ctx)
			if err != nil {
				m.l.Warning("File %d not found, skipping share %d", sourceId, s.ID)
				continue
			}

			// check if user exist
			if _, ok := m.state.UserIDs[int(s.UserID)]; !ok {
				m.l.Warning("User %d not found, skipping share %d", s.UserID, s.ID)
				continue
			}

			stm := tx.Share.Create().
				SetCreatedAt(formatTime(s.CreatedAt)).
				SetUpdatedAt(formatTime(s.UpdatedAt)).
				SetViews(s.Views).
				SetRawID(int(s.ID)).
				SetDownloads(s.Downloads).
				SetFileID(sourceId).
				SetUserID(int(s.UserID))

			if s.Password != "" {
				stm.SetPassword(s.Password)
			}

			if s.Expires != nil {
				stm.SetNillableExpires(s.Expires)
			}

			if s.RemainDownloads >= 0 {
				stm.SetRemainDownloads(s.RemainDownloads)
			}

			if _, err := stm.Save(ctx); err != nil {
				_ = tx.Rollback()
				return fmt.Errorf("failed to create share %d: %w", s.ID, err)
			}
		}

		if err := tx.Commit(); err != nil {
			return fmt.Errorf("failed to commit transaction: %w", err)
		}

		offset += batchSize
		m.state.ShareOffset = offset
		if err := m.saveState(); err != nil {
			m.l.Warning("Failed to save state after share batch: %s", err)
		} else {
			m.l.Info("Saved migration state after processing this batch")
		}
	}
	return nil
}
