package services

import (
	"context"
	"strings"
	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driven"
	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driving"
)

type appService struct {
	appRepo driven.AppRepository
}

func NewAppService(appRepo driven.AppRepository) driving.AppService {
	return &appService{appRepo: appRepo}
}

func (s *appService) CreateApp(ctx context.Context, region string, app *domain.App) error {
	// Validate inputs before delegating to repository
	if err := s.validateApp(app); err != nil {
		return err
	}

	if region == "" {
		return domain.ErrMissingRegion
	}

	if err := s.appRepo.CreateApp(ctx, region, app); err != nil {
		// Map repository errors to domain errors with context
		if domain.IsNotFoundError(err) {
			return domain.NewAppError(domain.ErrResourceConflict, "CreateApp", app.Key, region, 
				"app may already exist or have conflicting configuration")
		}
		return domain.NewAppError(domain.ErrInternalError, "CreateApp", app.Key, region, 
			"failed to create application").WithDetails(err.Error())
	}

	return nil
}

func (s *appService) ShowApp(ctx context.Context, region string, appKey domain.AppKey) (*domain.App, error) {
	if err := s.validateAppKey(appKey); err != nil {
		return nil, err
	}

	if region == "" {
		return nil, domain.ErrMissingRegion
	}

	app, err := s.appRepo.ShowApp(ctx, region, appKey)
	if err != nil {
		if domain.IsNotFoundError(err) {
			return nil, domain.NewAppError(domain.ErrResourceNotFound, "ShowApp", appKey, region, 
				"application does not exist")
		}
		return nil, domain.NewAppError(domain.ErrInternalError, "ShowApp", appKey, region, 
			"failed to retrieve application").WithDetails(err.Error())
	}

	return app, nil
}

func (s *appService) ShowApps(ctx context.Context, region string, appKey domain.AppKey) ([]domain.App, error) {
	if region == "" {
		return nil, domain.ErrMissingRegion
	}

	apps, err := s.appRepo.ShowApps(ctx, region, appKey)
	if err != nil {
		return nil, domain.NewAppError(domain.ErrInternalError, "ShowApps", appKey, region, 
			"failed to list applications").WithDetails(err.Error())
	}

	return apps, nil
}

func (s *appService) DeleteApp(ctx context.Context, region string, appKey domain.AppKey) error {
	if err := s.validateAppKey(appKey); err != nil {
		return err
	}

	if region == "" {
		return domain.ErrMissingRegion
	}

	if err := s.appRepo.DeleteApp(ctx, region, appKey); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewAppError(domain.ErrResourceNotFound, "DeleteApp", appKey, region, 
				"application does not exist")
		}
		return domain.NewAppError(domain.ErrInternalError, "DeleteApp", appKey, region, 
			"failed to delete application").WithDetails(err.Error())
	}

	return nil
}

func (s *appService) UpdateApp(ctx context.Context, region string, app *domain.App) error {
	if err := s.validateApp(app); err != nil {
		return err
	}

	if region == "" {
		return domain.ErrMissingRegion
	}

	if err := s.appRepo.UpdateApp(ctx, region, app); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewAppError(domain.ErrResourceNotFound, "UpdateApp", app.Key, region, 
				"application does not exist")
		}
		return domain.NewAppError(domain.ErrInternalError, "UpdateApp", app.Key, region, 
			"failed to update application").WithDetails(err.Error())
	}

	return nil
}

// validateApp performs business logic validation on an app
func (s *appService) validateApp(app *domain.App) error {
	if app == nil {
		return domain.NewDomainError(domain.ErrValidationFailed, "application cannot be nil")
	}

	if err := s.validateAppKey(app.Key); err != nil {
		return err
	}

	if strings.TrimSpace(app.ImagePath) == "" {
		return domain.NewDomainError(domain.ErrValidationFailed, "image path is required")
	}

	if strings.TrimSpace(app.Deployment) == "" {
		return domain.NewDomainError(domain.ErrValidationFailed, "deployment type is required")
	}

	return nil
}

// validateAppKey validates an application key
func (s *appService) validateAppKey(key domain.AppKey) error {
	if strings.TrimSpace(key.Organization) == "" {
		return domain.ErrInvalidAppKey.WithDetails("organization is required")
	}

	if strings.TrimSpace(key.Name) == "" {
		return domain.ErrInvalidAppKey.WithDetails("name is required")
	}

	if strings.TrimSpace(key.Version) == "" {
		return domain.ErrInvalidAppKey.WithDetails("version is required")
	}

	return nil
}
