package processor

import (
	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/repository"
	"context"
	"fmt"

	"github.com/google/uuid"
)

type OutletSettingProcessorImpl struct {
	outletSettingRepo *repository.OutletSettingRepositoryImpl
	outletRepo        *repository.OutletRepositoryImpl
}

func NewOutletSettingProcessorImpl(
	outletSettingRepo *repository.OutletSettingRepositoryImpl,
	outletRepo *repository.OutletRepositoryImpl,
) *OutletSettingProcessorImpl {
	return &OutletSettingProcessorImpl{
		outletSettingRepo: outletSettingRepo,
		outletRepo:        outletRepo,
	}
}

func (p *OutletSettingProcessorImpl) CreateSetting(ctx context.Context, req *models.CreateOutletSettingRequest) (*models.OutletSettingResponse, error) {
	// Check if outlet exists
	_, err := p.outletRepo.GetByID(ctx, req.OutletID)
	if err != nil {
		return nil, fmt.Errorf("outlet not found: %w", err)
	}

	// Check if setting already exists
	existingSetting, err := p.outletSettingRepo.GetByOutletIDAndKey(ctx, req.OutletID, req.Key)
	if err == nil && existingSetting != nil {
		return nil, fmt.Errorf("setting with key '%s' already exists for this outlet", req.Key)
	}

	setting := &entities.OutletSetting{
		OutletID: req.OutletID,
		Key:      req.Key,
		Value:    req.Value,
	}

	err = p.outletSettingRepo.Create(ctx, setting)
	if err != nil {
		return nil, fmt.Errorf("failed to create setting: %w", err)
	}

	return &models.OutletSettingResponse{
		ID:        setting.ID,
		OutletID:  setting.OutletID,
		Key:       setting.Key,
		Value:     setting.Value,
		CreatedAt: setting.CreatedAt,
		UpdatedAt: setting.UpdatedAt,
	}, nil
}

func (p *OutletSettingProcessorImpl) UpdateSetting(ctx context.Context, outletID uuid.UUID, key string, req *models.UpdateOutletSettingRequest) (*models.OutletSettingResponse, error) {
	// Check if outlet exists
	_, err := p.outletRepo.GetByID(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("outlet not found: %w", err)
	}

	// Get existing setting
	setting, err := p.outletSettingRepo.GetByOutletIDAndKey(ctx, outletID, key)
	if err != nil {
		return nil, fmt.Errorf("setting not found: %w", err)
	}

	// Update setting
	setting.Value = req.Value
	err = p.outletSettingRepo.Update(ctx, setting)
	if err != nil {
		return nil, fmt.Errorf("failed to update setting: %w", err)
	}

	return &models.OutletSettingResponse{
		ID:        setting.ID,
		OutletID:  setting.OutletID,
		Key:       setting.Key,
		Value:     setting.Value,
		CreatedAt: setting.CreatedAt,
		UpdatedAt: setting.UpdatedAt,
	}, nil
}

func (p *OutletSettingProcessorImpl) GetSetting(ctx context.Context, outletID uuid.UUID, key string) (*models.OutletSettingResponse, error) {
	// Check if outlet exists
	_, err := p.outletRepo.GetByID(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("outlet not found: %w", err)
	}

	setting, err := p.outletSettingRepo.GetByOutletIDAndKey(ctx, outletID, key)
	if err != nil {
		return nil, fmt.Errorf("setting not found: %w", err)
	}

	return &models.OutletSettingResponse{
		ID:        setting.ID,
		OutletID:  setting.OutletID,
		Key:       setting.Key,
		Value:     setting.Value,
		CreatedAt: setting.CreatedAt,
		UpdatedAt: setting.UpdatedAt,
	}, nil
}

func (p *OutletSettingProcessorImpl) GetPrinterSettings(ctx context.Context, outletID uuid.UUID) (*models.OutletPrinterSettings, error) {
	// Check if outlet exists
	outlet, err := p.outletRepo.GetByID(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("outlet not found: %w", err)
	}

	// Get printer settings from database
	settings, err := p.outletSettingRepo.GetPrinterSettingsByOutletID(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("failed to get printer settings: %w", err)
	}

	// Build printer settings with defaults
	printerSettings := &models.OutletPrinterSettings{
		OutletName:    p.getSettingValue(settings, constants.PRINTER_OUTLET_NAME, outlet.Name),
		Address:       p.getSettingValue(settings, constants.PRINTER_ADDRESS, ""),
		PhoneNumber:   p.getSettingValue(settings, constants.PRINTER_PHONE_NUMBER, ""),
		PaperSize:     p.getSettingValue(settings, constants.PRINTER_PAPER_SIZE, constants.DEFAULT_PAPER_SIZE),
		Footer:        p.getSettingValue(settings, constants.PRINTER_FOOTER, constants.DEFAULT_FOOTER),
		FooterHashtag: p.getSettingValue(settings, constants.PRINTER_FOOTER_HASHTAG, constants.DEFAULT_FOOTER_HASHTAG),
	}

	return printerSettings, nil
}

func (p *OutletSettingProcessorImpl) UpdatePrinterSettings(ctx context.Context, outletID uuid.UUID, req *models.UpdateOutletPrinterSettingsRequest) (*models.OutletPrinterSettings, error) {
	// Check if outlet exists
	_, err := p.outletRepo.GetByID(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("outlet not found: %w", err)
	}

	// Update each setting if provided
	if req.OutletName != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_OUTLET_NAME, *req.OutletName)
		if err != nil {
			return nil, fmt.Errorf("failed to update outlet name: %w", err)
		}
	}

	if req.Address != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_ADDRESS, *req.Address)
		if err != nil {
			return nil, fmt.Errorf("failed to update address: %w", err)
		}
	}

	if req.PhoneNumber != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_PHONE_NUMBER, *req.PhoneNumber)
		if err != nil {
			return nil, fmt.Errorf("failed to update phone number: %w", err)
		}
	}

	if req.PaperSize != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_PAPER_SIZE, *req.PaperSize)
		if err != nil {
			return nil, fmt.Errorf("failed to update paper size: %w", err)
		}
	}

	if req.Footer != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_FOOTER, *req.Footer)
		if err != nil {
			return nil, fmt.Errorf("failed to update footer: %w", err)
		}
	}

	if req.FooterHashtag != nil {
		err = p.upsertSetting(ctx, outletID, constants.PRINTER_FOOTER_HASHTAG, *req.FooterHashtag)
		if err != nil {
			return nil, fmt.Errorf("failed to update footer hashtag: %w", err)
		}
	}

	// Return updated settings
	return p.GetPrinterSettings(ctx, outletID)
}

func (p *OutletSettingProcessorImpl) DeleteSetting(ctx context.Context, outletID uuid.UUID, key string) error {
	// Check if outlet exists
	_, err := p.outletRepo.GetByID(ctx, outletID)
	if err != nil {
		return fmt.Errorf("outlet not found: %w", err)
	}

	err = p.outletSettingRepo.DeleteByOutletIDAndKey(ctx, outletID, key)
	if err != nil {
		return fmt.Errorf("failed to delete setting: %w", err)
	}

	return nil
}

func (p *OutletSettingProcessorImpl) getSettingValue(settings map[string]string, key, defaultValue string) string {
	if value, exists := settings[key]; exists {
		return value
	}
	return defaultValue
}

func (p *OutletSettingProcessorImpl) upsertSetting(ctx context.Context, outletID uuid.UUID, key, value string) error {
	setting, err := p.outletSettingRepo.GetByOutletIDAndKey(ctx, outletID, key)
	if err != nil {
		// Setting doesn't exist, create new one
		setting = &entities.OutletSetting{
			OutletID: outletID,
			Key:      key,
			Value:    value,
		}
		return p.outletSettingRepo.Create(ctx, setting)
	}

	// Setting exists, update it
	setting.Value = value
	return p.outletSettingRepo.Update(ctx, setting)
}
