package pickup_point

import (
	"context"
	"gorm.io/gorm/clause"

	"github.com/UpmeshLTD/urus-aio/infrastructure/common"
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"github.com/UpmeshLTD/urus-aio/shipment/db/gormdb"
	"github.com/UpmeshLTD/urus-aio/shipment/server/http/dto"
)

type Repository interface {
	GetListOfPickupPoints(ctx context.Context, workspaceID string, req *dto.GetPickupPointsRequest) ([]*entities.PickupPoint, int64, error)
	GetPickupPointByID(ctx context.Context, workspaceID string, id entities.ID) (*entities.PickupPoint, error)

	CreatePickupPoint(ctx context.Context, pickupPoint *entities.PickupPoint) (*entities.PickupPoint, error)
	UpdatePickupPoint(ctx context.Context, workspaceID string, id entities.ID, pickupPoint *entities.PickupPoint) error
	DeletePickupPoint(ctx context.Context, workspaceID string, id entities.ID) error
}

type repositoryImpl struct {
	db gormdb.GormDB
}

func NewRepository(db gormdb.GormDB) Repository {
	return &repositoryImpl{
		db: db,
	}
}

func (r *repositoryImpl) GetListOfPickupPoints(ctx context.Context, workspaceID string, req *dto.GetPickupPointsRequest) ([]*entities.PickupPoint, int64, error) {
	var (
		results    []*entities.PickupPoint
		builder    = r.db.DB().Where("workspace_id = ?", workspaceID)
		pagination = &req.Pagination
	)

	if !req.Deactivate {
		builder = builder.Where("deactivate = false")
	}

	builder.Scopes(common.Paginate(results, pagination, builder))

	if err := builder.Preload("Address.SubLevelLocations").Preload(clause.Associations).Find(&results).Error; err != nil {
		return nil, 0, err
	}
	return results, pagination.Total, nil
}

func (r *repositoryImpl) GetPickupPointByID(ctx context.Context, workspaceID string, id entities.ID) (*entities.PickupPoint, error) {
	res := &entities.PickupPoint{}
	if err := r.db.DB().Preload("Address.SubLevelLocations").Preload(clause.Associations).Where("workspace_id = ?", workspaceID).First(res, "id = ?", id.ToInt64()).Error; err != nil {
		return nil, err
	}
	return res, nil
}

func (r *repositoryImpl) CreatePickupPoint(ctx context.Context, pickupPoint *entities.PickupPoint) (*entities.PickupPoint, error) {
	tx := r.db.DB().Begin()

	if err := tx.Create(&pickupPoint).Error; err != nil {
		tx.Rollback()
		return nil, err
	}

	// Turn off default of all other pickup points
	if pickupPoint.Default {
		err := tx.Model(&entities.PickupPoint{}).Where("workspace_id = ? AND id <> ?", pickupPoint.WorkspaceID, pickupPoint.ID.ToInt64()).Update("default", false).Error
		if err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	return pickupPoint, tx.Commit().Error
}

func (r *repositoryImpl) UpdatePickupPoint(ctx context.Context, workspaceID string, id entities.ID, pickupPoint *entities.PickupPoint) error {
	tx := r.db.DB().Begin()

	pp := &entities.PickupPoint{}
	if err := tx.Preload(clause.Associations).Where("workspace_id = ?", workspaceID).First(&pp, "id = ?", id.ToInt64()).Error; err != nil {
		tx.Rollback()
		return err
	}

	// clear SubLevelLocations association
	err := tx.Model(&pp.Address).Association("SubLevelLocations").Clear()
	if err != nil {
		tx.Rollback()
		return err
	}

	pp.FirstName = pickupPoint.FirstName
	pp.LastName = pickupPoint.LastName
	pp.Email = pickupPoint.Email
	pp.PhoneNumber = pickupPoint.PhoneNumber

	pp.Address.Address1 = pickupPoint.Address.Address1
	pp.Address.Address2 = pickupPoint.Address.Address2
	pp.Address.PostalCode = pickupPoint.Address.PostalCode
	pp.Address.UnitNumber = pickupPoint.Address.UnitNumber
	pp.Address.SubLevelLocations = pickupPoint.Address.SubLevelLocations

	pp.Default = pickupPoint.Default
	pp.Deactivate = pickupPoint.Deactivate

	if err := tx.Save(&pp).Error; err != nil {
		tx.Rollback()
		return err
	}

	// Turn off default of all other pickup points
	if pickupPoint.Default {
		err := tx.Model(&entities.PickupPoint{}).Where("workspace_id = ? AND id <> ?", workspaceID, pp.ID.ToInt64()).Update("default", false).Error
		if err != nil {
			tx.Rollback()
			return err
		}
	}
	return tx.Commit().Error
}

func (r *repositoryImpl) DeletePickupPoint(ctx context.Context, workspaceID string, id entities.ID) error {
	if err := r.db.DB().Delete(&entities.PickupPoint{BaseEntity: entities.BaseEntity{ID: id}, WorkspaceID: workspaceID}).Error; err != nil {
		return err
	}
	return nil
}
