package usecases

import (
	"cargo-erp-backend/internal/domain"
	"cargo-erp-backend/internal/handlers/dto"
	"cargo-erp-backend/pkg/datatable"
	"cargo-erp-backend/pkg/helpers"
	"fmt"
	"reflect"
	"time"

	"gorm.io/gorm"
)

type CostComponentUsecaseInterface interface {
	GetAll(filter dto.CostComponentFilterAllRequest) ([]domain.CostComponent, int64, error)
	GetList(dto.DataTableRequest) (dto.DataTableResponse, error)
	GetByID(id string) (domain.CostComponent, error)
	Create(name string, code string, value string, componentType int32, unitType int32, createdBy string, merchantid *string) (domain.CostComponent, error)
	Update(id string, name string, code string, value string, componentType int32, unitType int32, updatedBy string) (domain.CostComponent, error)
	Delete(id string) error
	SortDown(id string) error
	SortUp(id string) error
}

type CostComponentUsecase struct {
	Helper   helpers.HelperInterface
	AuthUser domain.User
}

func NewCostComponentUsecase(helper helpers.HelperInterface, authUser domain.User) CostComponentUsecaseInterface {
	return &CostComponentUsecase{Helper: helper, AuthUser: authUser}
}

func (u *CostComponentUsecase) GetAll(filter dto.CostComponentFilterAllRequest) ([]domain.CostComponent, int64, error) {
	var components []domain.CostComponent
	var total int64

	db := u.Helper.GetDB("slave")
	tx := db.Model(&components)
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
		tx = tx.Where(db.Where("merchant_id IS NULL").Or("merchant_id=?", u.AuthUser.MerchantID))
	}
	v := reflect.ValueOf(filter)
	t := reflect.TypeOf(filter)

	for i := 0; i < v.NumField(); i++ {
		if v.Field(i).String() != "" {
			tagKey := t.Field(i).Tag.Get("form")
			tx = tx.Where(fmt.Sprintf("%v=?", tagKey), v.Field(i))
		}
	}
	tx.Model(&domain.CostComponent{}).Count(&total)
	tx.Order("inc ASC, name ASC")
	if err := tx.Find(&components).Error; err != nil {
		return nil, 0, err
	}
	return components, total, nil
}

func (u *CostComponentUsecase) GetList(req dto.DataTableRequest) (dto.DataTableResponse, error) {
	var response dto.DataTableResponse
	db := u.Helper.GetDB("slave")
	tx := db.Table("cost_component").
		Joins("LEFT JOIN merchant ON cost_component.merchant_id=merchant.id")
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
		tx = tx.Where("cost_component.merchant_id IS NULL OR cost_component.merchant_id=?", u.AuthUser.MerchantID)
	}
	dt := datatable.NewDatatable(tx, req)
	coldef := make([]dto.DataTableColDef, 0)
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.name",
		Alias: "name",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.code",
		Alias: "code",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.inc",
		Alias: "inc",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.value",
		Alias: "value",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.component_type",
		Alias: "component_type",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "merchant.id",
		Alias: "merchant_id",
	})
	coldef = append(coldef, dto.DataTableColDef{
		Field: "cost_component.id",
		Alias: "id",
	})

	response = dt.Render(coldef)
	return response, nil
}

func (u *CostComponentUsecase) GetByID(id string) (domain.CostComponent, error) {
	var component domain.CostComponent
	db := u.Helper.GetDB("slave")
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
		db = db.Where("merchant_id IS NULL OR merchant_id=?", u.AuthUser.MerchantID)
	}
	if err := db.Where("id = ?", id).First(&component).Error; err != nil {
		return domain.CostComponent{}, err
	}
	return component, nil
}

func (u *CostComponentUsecase) Create(name string, code string, value string, componentType int32, unitType int32, createdBy string, merchantid *string) (domain.CostComponent, error) {
	now := time.Now()

	db := u.Helper.GetDB("master")

	var component domain.CostComponent
	err := db.Transaction(func(tx *gorm.DB) error {
		var maxInc int64
		q := tx.Model(&domain.CostComponent{})
		if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
			q = q.Where("merchant_id=?", u.AuthUser.MerchantID)
		}
		q.Select("COALESCE(MAX(inc),0)").Row().Scan(&maxInc)

		inc := int32(maxInc + 1)

		component = domain.CostComponent{
			Name:          &name,
			Code:          &code,
			Inc:           &inc,
			Value:         &value,
			ComponentType: componentType,
			UnitType:      unitType,
			MerchantID:    merchantid,
			CreatedBy:     &createdBy,
			CreatedOn:     &now,
			UpdatedBy:     &createdBy,
			UpdatedOn:     &now,
		}

		return tx.Create(&component).Error
	})
	if err != nil {
		return domain.CostComponent{}, err
	}
	logUsecase := NewActivityLogUsecase(u.Helper, u.AuthUser)
	logUsecase.LogCreate("cost_component", component, merchantid)
	return component, nil
}

func (u *CostComponentUsecase) Update(id string, name string, code string, value string, componentType int32, unitType int32, updatedBy string) (domain.CostComponent, error) {
	var component domain.CostComponent
	var oldComponent domain.CostComponent
	db := u.Helper.GetDB("master")
	err := db.Transaction(func(tx *gorm.DB) error {
		q := tx
		if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
			q = tx.Where("merchant_id=?", u.AuthUser.MerchantID)
		}
		if err := q.Where("id = ?", id).First(&component).Error; err != nil {
			return err
		}
		oldComponent = component
		now := time.Now()
		component.Name = &name
		component.Code = &code
		component.Value = &value
		component.ComponentType = componentType
		component.UnitType = unitType
		component.UpdatedBy = &updatedBy
		component.UpdatedOn = &now
		return tx.Save(&component).Error
	})
	if err != nil {
		return domain.CostComponent{}, err
	}
	var merchantID *string
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
		merchantID = u.AuthUser.MerchantID
	}
	logUsecase := NewActivityLogUsecase(u.Helper, u.AuthUser)
	logUsecase.LogUpdate("cost_component", oldComponent, component, merchantID)
	return component, nil
}

func (u *CostComponentUsecase) Delete(id string) error {
	db := u.Helper.GetDB("master")
	return db.Transaction(func(tx *gorm.DB) error {
		var component domain.CostComponent
		q := tx
		if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
			q = tx.Where("merchant_id=?", u.AuthUser.MerchantID)
		}
		if err := q.Where("id = ?", id).First(&component).Error; err != nil {
			return err
		}
		var merchantID *string
		if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {
			merchantID = u.AuthUser.MerchantID
		}
		logUsecase := NewActivityLogUsecase(u.Helper, u.AuthUser)
		logUsecase.LogDelete("cost_component", id, component, merchantID)
		if err := tx.Delete(&domain.CostComponent{}, "id = ?", id).Error; err != nil {
			return err
		}
		return nil
	})
}

func (u *CostComponentUsecase) SortDown(id string) error {
	db := u.Helper.GetDB("master")
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {

		var olData domain.CostComponent
		err := db.Model(&olData).Where("id=?", id).Where("merchant_id=?", u.AuthUser.MerchantID).First(&olData).Error
		if err != nil {
			return err
		}
		if olData.ID != "" {
			var newData domain.CostComponent
			db.Model(&newData).Where("merchant_id=?", u.AuthUser.MerchantID).Where("inc<?", olData.Inc).Order("inc DESC").First(&newData)
			if newData.ID != "" {
				err := db.Transaction(func(tx *gorm.DB) error {
					err := tx.Model(&olData).Where("id=?", olData.ID).UpdateColumn("inc", newData.Inc).Error
					if err != nil {
						return err
					}
					err = tx.Model(&newData).Where("id=?", newData.ID).UpdateColumn("inc", olData.Inc).Error
					if err != nil {
						return err
					}
					return nil
				})
				return err
			}
		}
	} else {
		var olData domain.CostComponent
		err := db.Model(&olData).Where("id=?", id).First(&olData).Error
		if err != nil {
			return err
		}
		if olData.ID != "" {
			merchant_id := olData.MerchantID
			var newData domain.CostComponent
			db.Model(&newData).Where("merchant_id=?", merchant_id).Where("inc<?", olData.Inc).Order("inc DESC").First(&newData)
			if newData.ID != "" {
				err := db.Transaction(func(tx *gorm.DB) error {
					err := tx.Model(&olData).Where("id=?", olData.ID).UpdateColumn("inc", newData.Inc).Error
					if err != nil {
						return err
					}
					err = tx.Model(&newData).Where("id=?", newData.ID).UpdateColumn("inc", olData.Inc).Error
					if err != nil {
						return err
					}
					return nil
				})
				return err
			}
		}
	}

	return nil
}

func (u *CostComponentUsecase) SortUp(id string) error {
	db := u.Helper.GetDB("master")
	if u.AuthUser.UsersRole_Relation == nil || u.AuthUser.UsersRole_Relation.RoleKey != "SPM" {

		var olData domain.CostComponent
		err := db.Model(&olData).Where("id=?", id).Where("merchant_id=?", u.AuthUser.MerchantID).First(&olData).Error
		if err != nil {
			return err
		}
		if olData.ID != "" {
			var newData domain.CostComponent
			db.Model(&newData).Where("merchant_id=?", u.AuthUser.MerchantID).Where("inc>?", olData.Inc).Order("inc ASC").Limit(1).First(&newData)
			if newData.ID != "" {
				err := db.Transaction(func(tx *gorm.DB) error {
					err := tx.Model(&olData).Where("id=?", olData.ID).UpdateColumn("inc", newData.Inc).Error
					if err != nil {
						return err
					}
					err = tx.Model(&newData).Where("id=?", newData.ID).UpdateColumn("inc", olData.Inc).Error
					if err != nil {
						return err
					}
					return nil
				})
				return err
			}
		}
	} else {
		var olData domain.CostComponent
		err := db.Model(&olData).Where("id=?", id).First(&olData).Error
		if err != nil {
			return err
		}
		if olData.ID != "" {
			merchant_id := olData.MerchantID
			var newData domain.CostComponent
			db.Model(&newData).Where("merchant_id=?", merchant_id).Where("inc>?", olData.Inc).Order("inc ASC").Limit(1).First(&newData)
			if newData.ID != "" {
				err := db.Transaction(func(tx *gorm.DB) error {
					err := tx.Model(&olData).Where("id=?", olData.ID).UpdateColumn("inc", newData.Inc).Error
					if err != nil {
						return err
					}
					err = tx.Model(&newData).Where("id=?", newData.ID).UpdateColumn("inc", olData.Inc).Error
					if err != nil {
						return err
					}
					return nil
				})
				return err
			}
		}
	}

	return nil
}
