package model

import (
	"time"

	"github.com/pingcap/errors"
	"gorm.io/gorm"

	"github.com/pingcap-inc/clinic/pkg/db"
)

func UpdateUploadStatus(fileUUID string, status db.StatusType) error {
	return db.Transaction(func(tx *gorm.DB) error {
		return db.NewUploadFileQuerySet(tx).
			FileUUIDEq(fileUUID).GetUpdater().
			SetUploadStatus(string(status)).Update()
	})
}

func QueryMaxFileUploadedPart(fileUUID string) (*db.UploadFilePart, error) {
	var ufp []db.UploadFilePart
	err := db.Transaction(func(tx *gorm.DB) error {
		err := tx.Where("file_uuid=?", fileUUID).Order("serial_number asc").Find(&ufp).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if len(ufp) == 0 {
		return nil, nil
	}

	//if ufp[0].SerialNumber != 1 {
	//
	//}

	for i := 0; i < len(ufp); i++ {
		if ufp[i].SerialNumber != i+1 {
			if i == 0 {
				return nil, nil
			}

			return &ufp[i-1], nil
		}
	}

	//for i := 0; i < len(ufp)
	//for i := 1; i < len(ufp); i++ {
	//	if ufp[i].SerialNumber-1 != ufp[i-1].SerialNumber {
	//		return &ufp[i-1], nil
	//	}
	//}

	return &ufp[len(ufp)-1], nil
}

func UpsertUploadFilePart(fileUUID string, serialNumber int, etag string, partLen uint64) error {
	uf, err := QueryUploadFile(fileUUID)
	if err != nil {
		return err
	}

	if uf == nil {
		return errors.Errorf("the upload file record is not existed, file=%s", fileUUID)
	}

	if uf != nil && uf.UploadStatus == string(db.StatusSuccess) {
		return errors.Errorf("the file is completed, file=%s", fileUUID)
	}

	return db.Transaction(func(txn *gorm.DB) error {
		var ufp db.UploadFilePart
		err := txn.Where("file_uuid=? and serial_number=?", fileUUID, serialNumber).First(&ufp).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}

		if ufp.ID > 0 {
			return txn.Model(&ufp).Updates(map[string]any{"etag": etag, "updated_at": time.Now()}).Error
		}

		ufp = db.UploadFilePart{
			Model: gorm.Model{
				CreatedAt: time.Now(),
				UpdatedAt: time.Now(),
			},
			FileUUID:     fileUUID,
			SerialNumber: serialNumber,
			Etag:         etag,
			UploadFileID: uf.ID,
			PartLen:      partLen,
		}

		return txn.Create(&ufp).Error
	})
}

func QueryUploadFilePart(fileUUID string, serialNumber int) (*db.UploadFilePart, error) {
	var ufp db.UploadFilePart
	err := db.Transaction(func(txn *gorm.DB) error {
		err := txn.Where("file_uuid=? and serial_number=?", fileUUID, serialNumber).First(&ufp).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if ufp.ID == 0 {
		return nil, nil
	}

	return &ufp, nil
}

func QueryUploadFileParts(fileUUID string) ([]db.UploadFilePart, error) {
	var ufps []db.UploadFilePart
	err := db.Transaction(func(tx *gorm.DB) error {
		return tx.Where("file_uuid=?", fileUUID).Order("serial_number asc").Find(&ufps).Error
	})

	return ufps, err
}

func DeleteUploadFileParts(fileUUID string) error {
	return db.Transaction(func(tx *gorm.DB) error {
		return tx.Where("file_uuid=?", fileUUID).Unscoped().Delete(&db.UploadFilePart{}).Error
	})
}
