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 appInstanceService struct {
	appInstanceRepo driven.AppInstanceRepository
}

func NewAppInstanceService(appInstanceRepo driven.AppInstanceRepository) driving.AppInstanceService {
	return &appInstanceService{appInstanceRepo: appInstanceRepo}
}

func (s *appInstanceService) CreateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
	if err := s.validateAppInstance(appInst); err != nil {
		return err
	}

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

	if err := s.appInstanceRepo.CreateAppInstance(ctx, region, appInst); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewInstanceError(domain.ErrResourceConflict, "CreateAppInstance", appInst.Key, region, 
				"app instance may already exist or have conflicting configuration")
		}
		return domain.NewInstanceError(domain.ErrInternalError, "CreateAppInstance", appInst.Key, region, 
			"failed to create app instance").WithDetails(err.Error())
	}

	return nil
}

func (s *appInstanceService) ShowAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) (*domain.AppInstance, error) {
	if err := s.validateAppInstanceKey(appInstKey); err != nil {
		return nil, err
	}

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

	instance, err := s.appInstanceRepo.ShowAppInstance(ctx, region, appInstKey)
	if err != nil {
		if domain.IsNotFoundError(err) {
			return nil, domain.NewInstanceError(domain.ErrResourceNotFound, "ShowAppInstance", appInstKey, region, 
				"app instance does not exist")
		}
		return nil, domain.NewInstanceError(domain.ErrInternalError, "ShowAppInstance", appInstKey, region, 
			"failed to retrieve app instance").WithDetails(err.Error())
	}

	return instance, nil
}

func (s *appInstanceService) ShowAppInstances(ctx context.Context, region string, appInstKey domain.AppInstanceKey) ([]domain.AppInstance, error) {
	if region == "" {
		return nil, domain.ErrMissingRegion
	}

	instances, err := s.appInstanceRepo.ShowAppInstances(ctx, region, appInstKey)
	if err != nil {
		return nil, domain.NewInstanceError(domain.ErrInternalError, "ShowAppInstances", appInstKey, region, 
			"failed to list app instances").WithDetails(err.Error())
	}

	return instances, nil
}

func (s *appInstanceService) DeleteAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
	if err := s.validateAppInstanceKey(appInstKey); err != nil {
		return err
	}

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

	if err := s.appInstanceRepo.DeleteAppInstance(ctx, region, appInstKey); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewInstanceError(domain.ErrResourceNotFound, "DeleteAppInstance", appInstKey, region, 
				"app instance does not exist")
		}
		return domain.NewInstanceError(domain.ErrInternalError, "DeleteAppInstance", appInstKey, region, 
			"failed to delete app instance").WithDetails(err.Error())
	}

	return nil
}

func (s *appInstanceService) UpdateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
	if err := s.validateAppInstance(appInst); err != nil {
		return err
	}

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

	if err := s.appInstanceRepo.UpdateAppInstance(ctx, region, appInst); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewInstanceError(domain.ErrResourceNotFound, "UpdateAppInstance", appInst.Key, region, 
				"app instance does not exist")
		}
		return domain.NewInstanceError(domain.ErrInternalError, "UpdateAppInstance", appInst.Key, region, 
			"failed to update app instance").WithDetails(err.Error())
	}

	return nil
}

func (s *appInstanceService) RefreshAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
	if err := s.validateAppInstanceKey(appInstKey); err != nil {
		return err
	}

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

	if err := s.appInstanceRepo.RefreshAppInstance(ctx, region, appInstKey); err != nil {
		if domain.IsNotFoundError(err) {
			return domain.NewInstanceError(domain.ErrResourceNotFound, "RefreshAppInstance", appInstKey, region, 
				"app instance does not exist")
		}
		return domain.NewInstanceError(domain.ErrInternalError, "RefreshAppInstance", appInstKey, region, 
			"failed to refresh app instance").WithDetails(err.Error())
	}

	return nil
}

// validateAppInstance performs business logic validation on an app instance
func (s *appInstanceService) validateAppInstance(appInst *domain.AppInstance) error {
	if appInst == nil {
		return domain.NewDomainError(domain.ErrValidationFailed, "app instance cannot be nil")
	}

	if err := s.validateAppInstanceKey(appInst.Key); err != nil {
		return err
	}

	// Validate flavor if present
	if strings.TrimSpace(appInst.Flavor.Name) == "" {
		return domain.NewDomainError(domain.ErrValidationFailed, "flavor name is required")
	}

	return nil
}

// validateAppInstanceKey validates an app instance key
func (s *appInstanceService) validateAppInstanceKey(key domain.AppInstanceKey) error {
	if strings.TrimSpace(key.Organization) == "" {
		return domain.ErrInvalidInstanceKey.WithDetails("organization is required")
	}

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

	// Validate embedded cloudlet key
	if strings.TrimSpace(key.CloudletKey.Organization) == "" {
		return domain.ErrInvalidInstanceKey.WithDetails("cloudlet organization is required")
	}

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

	return nil
}
