package processor

import (
	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/mappers"
	"apskel-pos-be/internal/models"
	"context"
	"time"

	"github.com/google/uuid"
)

type UnitProcessorImpl struct {
	unitRepo UnitRepository
}

func NewUnitProcessor(unitRepo UnitRepository) *UnitProcessorImpl {
	return &UnitProcessorImpl{
		unitRepo: unitRepo,
	}
}

func (p *UnitProcessorImpl) CreateUnit(ctx context.Context, req *models.CreateUnitRequest) (*models.UnitResponse, error) {
	unit := &entities.Unit{
		ID:             uuid.New(),
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		Name:           req.Name,
		Abbreviation:   req.Abbreviation,
		IsActive:       req.IsActive,
		CreatedAt:      time.Now(),
		UpdatedAt:      time.Now(),
	}

	err := p.unitRepo.Create(ctx, unit)
	if err != nil {
		return nil, err
	}

	unitModel := mappers.MapUnitEntityToModel(unit)
	response := &models.UnitResponse{
		ID:             unitModel.ID,
		OrganizationID: unitModel.OrganizationID,
		OutletID:       unitModel.OutletID,
		Name:           unitModel.Name,
		Abbreviation:   unitModel.Abbreviation,
		IsActive:       unitModel.IsActive,
		DeletedAt:      unitModel.DeletedAt,
		CreatedAt:      unitModel.CreatedAt,
		UpdatedAt:      unitModel.UpdatedAt,
	}

	return response, nil
}

func (p *UnitProcessorImpl) GetUnitByID(ctx context.Context, id uuid.UUID) (*models.UnitResponse, error) {
	contextInfo := appcontext.FromGinContext(ctx)
	organizationID := contextInfo.OrganizationID

	unit, err := p.unitRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, err
	}

	unitModel := mappers.MapUnitEntityToModel(unit)
	response := &models.UnitResponse{
		ID:             unitModel.ID,
		OrganizationID: unitModel.OrganizationID,
		OutletID:       unitModel.OutletID,
		Name:           unitModel.Name,
		Abbreviation:   unitModel.Abbreviation,
		IsActive:       unitModel.IsActive,
		DeletedAt:      unitModel.DeletedAt,
		CreatedAt:      unitModel.CreatedAt,
		UpdatedAt:      unitModel.UpdatedAt,
	}

	return response, nil
}

func (p *UnitProcessorImpl) ListUnits(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) (*models.PaginatedResponse[models.UnitResponse], error) {
	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 10
	}
	if limit > 100 {
		limit = 100
	}

	units, total, err := p.unitRepo.GetAll(ctx, organizationID, outletID, page, limit, search)
	if err != nil {
		return nil, err
	}

	unitModels := mappers.MapUnitEntitiesToModels(units)
	unitResponses := make([]models.UnitResponse, len(unitModels))

	for i, unitModel := range unitModels {
		unitResponses[i] = models.UnitResponse{
			ID:             unitModel.ID,
			OrganizationID: unitModel.OrganizationID,
			OutletID:       unitModel.OutletID,
			Name:           unitModel.Name,
			Abbreviation:   unitModel.Abbreviation,
			IsActive:       unitModel.IsActive,
			DeletedAt:      unitModel.DeletedAt,
			CreatedAt:      unitModel.CreatedAt,
			UpdatedAt:      unitModel.UpdatedAt,
		}
	}

	paginatedResponse := &models.PaginatedResponse[models.UnitResponse]{
		Data: unitResponses,
		Pagination: models.Pagination{
			Page:       page,
			Limit:      limit,
			Total:      int64(total),
			TotalPages: (total + limit - 1) / limit,
		},
	}

	return paginatedResponse, nil
}

func (p *UnitProcessorImpl) UpdateUnit(ctx context.Context, id uuid.UUID, req *models.UpdateUnitRequest) (*models.UnitResponse, error) {
	contextInfo := appcontext.FromGinContext(ctx)
	organizationID := contextInfo.OrganizationID

	existingUnit, err := p.unitRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, err
	}

	existingUnit.OutletID = req.OutletID
	existingUnit.Name = req.Name
	existingUnit.Abbreviation = req.Abbreviation
	existingUnit.IsActive = req.IsActive
	existingUnit.UpdatedAt = time.Now()

	err = p.unitRepo.Update(ctx, existingUnit)
	if err != nil {
		return nil, err
	}

	unitModel := mappers.MapUnitEntityToModel(existingUnit)
	response := &models.UnitResponse{
		ID:             unitModel.ID,
		OrganizationID: unitModel.OrganizationID,
		OutletID:       unitModel.OutletID,
		Name:           unitModel.Name,
		Abbreviation:   unitModel.Abbreviation,
		IsActive:       unitModel.IsActive,
		DeletedAt:      unitModel.DeletedAt,
		CreatedAt:      unitModel.CreatedAt,
		UpdatedAt:      unitModel.UpdatedAt,
	}

	return response, nil
}

func (p *UnitProcessorImpl) DeleteUnit(ctx context.Context, id uuid.UUID) error {
	contextInfo := appcontext.FromGinContext(ctx)
	organizationID := contextInfo.OrganizationID

	err := p.unitRepo.Delete(ctx, id, organizationID)
	if err != nil {
		return err
	}

	return nil
}
