/*
DESCRIPTION:
Any functions that are used internally by the use-case
*/
package uploadfile

import (
	"fmt"
	"path/filepath"
	ecore "simrs-vx/internal/domain/base-entities/core"
	ere "simrs-vx/internal/domain/references/encounter"
	"strings"
	"time"

	pl "simrs-vx/pkg/logger"
	pmh "simrs-vx/pkg/minio-helper"
	puh "simrs-vx/pkg/upload-helper"

	dg "github.com/karincake/apem/db-gorm-pg"
	"gorm.io/gorm"

	ee "simrs-vx/internal/domain/main-entities/encounter-document"
	ep "simrs-vx/internal/domain/main-entities/person"

	ue "simrs-vx/internal/use-case/main-use-case/encounter-document"
	up "simrs-vx/internal/use-case/main-use-case/person"
)

func uploadAndGenerateFileUrl(input CreateDto, event *pl.Event) (string, error) {
	pl.SetLogInfo(event, input, "started", "uploadAndGenerateFileUrl")
	bucket := string(input.EntityType_Code)
	ext := strings.ToLower(filepath.Ext(input.Filename))

	if !puh.IsValidFileType(ext, bucket) {
		return "", fmt.Errorf("invalid file type: %s", input.Filename)
	}
	objectName := fmt.Sprintf("%v/%s-%d%s", *input.Ref_Id, input.Type_Code, time.Now().UnixNano(), ext)

	uploadInput := pmh.UploadReaderInput{
		BucketName:  bucket,
		Name:        objectName,
		File:        input.File,
		Size:        input.Size,
		ContentType: input.MimeType,
	}

	_, err := pmh.I.PutObject(uploadInput)
	if err != nil {
		return "", err
	}

	// Build URL for access
	publicUrl := pmh.I.GenerateUrl(bucket, objectName)

	pl.SetLogInfo(event, nil, "complete")
	return publicUrl, nil
}

func removeUploadedFile(bucket, fileUrl string, refId uint, event *pl.Event) error {
	pl.SetLogInfo(event, nil, "started", "removeUploadedFile")
	filename, err := pmh.GetFilename(fileUrl)
	if err != nil {
		return err
	}

	filePath := fmt.Sprintf("%v/%s", refId, filename)
	err = pmh.I.RemoveObject(bucket, filePath)
	if err != nil {
		return err
	}
	pl.SetLogInfo(event, nil, "complete")
	return nil
}

func updatePersonAttachment(dataPerson *ep.Person, event *pl.Event, dbx ...*gorm.DB) error {
	pl.SetLogInfo(event, dataPerson, "started", "DBUpdate")

	var tx *gorm.DB
	if len(dbx) > 0 {
		tx = dbx[0]
	} else {
		tx = dg.I
	}

	if err := tx.Save(&dataPerson).Error; err != nil {
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-update-fail",
			Detail: "Database update failed",
			Raw:    err,
		}
		return pl.SetLogError(event, dataPerson)
	}

	pl.SetLogInfo(event, nil, "complete")
	return nil
}

func setPersonAttachment(input CreateDto, event *pl.Event, tx *gorm.DB) (*ep.Person, error) {
	// get person
	dataPerson, err := up.ReadDetailData(ep.ReadDetailDto{Id: *input.Ref_Id}, event, tx)
	if err != nil {
		return nil, err
	}

	var removeUrl string
	switch input.Type_Code {
	case ere.DTCPRN:
		if dataPerson.ResidentIdentityFileUrl != nil {
			removeUrl = *dataPerson.ResidentIdentityFileUrl
		}
		dataPerson.ResidentIdentityFileUrl = &input.FilePath
	case ere.DTCPDL:
		if dataPerson.DrivingLicenseFileUrl != nil {
			removeUrl = *dataPerson.DrivingLicenseFileUrl
		}
		dataPerson.DrivingLicenseFileUrl = &input.FilePath
	case ere.DTCPP:
		if dataPerson.PassportFileUrl != nil {
			removeUrl = *dataPerson.PassportFileUrl
		}
		dataPerson.PassportFileUrl = &input.FilePath
	case ere.DTCPFC:
		if dataPerson.FamilyIdentityFileUrl != nil {
			removeUrl = *dataPerson.FamilyIdentityFileUrl
		}
		dataPerson.FamilyIdentityFileUrl = &input.FilePath
	}

	if removeUrl != "" {
		if err := removeUploadedFile(string(input.EntityType_Code), removeUrl, *input.Ref_Id, event); err != nil {
			return nil, err
		}
	}

	if err = updatePersonAttachment(dataPerson, event, tx); err != nil {
		return nil, err
	}

	return dataPerson, nil
}

func setEncounterDocument(input CreateDto, event *pl.Event, tx *gorm.DB) (*ee.EncounterDocument, error) {
	data := ee.EncounterDocument{}

	// get EncounterDocument
	dataUpload, _, err := ue.ReadListData(ee.ReadListDto{
		FilterDto: ee.FilterDto{
			Encounter_Id: input.Ref_Id,
			Type_Code:    input.Type_Code},
		Pagination: ecore.Pagination{PageSize: 1}}, event, tx)
	if err != nil {
		return nil, err
	}

	// set data
	createDto := ee.CreateDto{
		Encounter_Id:       input.Ref_Id,
		Type_Code:          input.Type_Code,
		Name:               input.Name,
		FilePath:           input.FilePath,
		Filename:           input.Filename,
		Upload_Employee_Id: input.Upload_Employee_Id,
	}

	if input.Type_Code == ere.DTCSEP || input.Type_Code == ere.DTCSIPP {
		if len(dataUpload) > 0 {
			data = dataUpload[0]

			// remove file
			if err := removeUploadedFile(string(input.EntityType_Code), *data.FilePath, *data.Encounter_Id, event); err != nil {
				return nil, err
			}

			// update data
			err = ue.UpdateData(ee.UpdateDto{
				Id:        uint16(data.Id),
				CreateDto: createDto}, &data, event, tx)
			if err != nil {
				return nil, err
			}

			data.FilePath = &input.FilePath

			return &data, nil
		}
	}

	// insert data
	resData, err := ue.CreateData(createDto, event, tx)
	if err != nil {
		return nil, err
	}
	data = *resData

	return &data, nil
}
