/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.quarkus.core;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Supplier;

import io.quarkus.arc.Arc;
import jakarta.enterprise.inject.spi.BeanManager;
import org.apache.camel.CamelContext;
import org.apache.camel.Component;
import org.apache.camel.Endpoint;
import org.apache.camel.Route;
import org.apache.camel.Service;
import org.apache.camel.VetoCamelContextStartException;
import org.apache.camel.quarkus.core.events.ComponentAddEvent;
import org.apache.camel.quarkus.core.events.ComponentRemoveEvent;
import org.apache.camel.quarkus.core.events.EndpointAddEvent;
import org.apache.camel.quarkus.core.events.EndpointRemoveEvent;
import org.apache.camel.quarkus.core.events.ErrorHandlerAddEvent;
import org.apache.camel.quarkus.core.events.ErrorHandlerRemoveEvent;
import org.apache.camel.quarkus.core.events.ServiceAddEvent;
import org.apache.camel.quarkus.core.events.ServiceRemoveEvent;
import org.apache.camel.quarkus.core.events.ThreadPoolAddEvent;
import org.apache.camel.quarkus.core.events.ThreadPoolRemoveEvent;
import org.apache.camel.spi.CamelEvent;
import org.apache.camel.spi.LifecycleStrategy;
import org.apache.camel.util.function.Suppliers;

/**
 * Bridges {@link org.apache.camel.spi.LifecycleStrategy} callbacks and CDI by producing the correspondent
 * events. Events are only fired if a corresponding CDI observer is configured for them.
 * <p>
 * Note that this class does not implement all the callback as some notifications them are already covered
 * by management events {@link CamelManagementEventBridge}
 * <p>
 *
 * @see ComponentAddEvent
 * @see ComponentRemoveEvent
 * @see EndpointAddEvent
 * @see EndpointRemoveEvent
 * @see ErrorHandlerAddEvent
 * @see ErrorHandlerRemoveEvent
 * @see ServiceAddEvent
 * @see ServiceRemoveEvent
 * @see ThreadPoolAddEvent
 * @see ThreadPoolRemoveEvent
 */
public class CamelLifecycleEventBridge implements LifecycleStrategy {
    private final Supplier<BeanManager> beanManager;
    private final Set<String> observedLifecycleEvents;

    public CamelLifecycleEventBridge(Set<String> observedLifecycleEvents) {
        this.beanManager = Suppliers.memorize(Arc.container()::beanManager);
        this.observedLifecycleEvents = observedLifecycleEvents;
    }

    private <T extends CamelEvent> void fireEvent(T event) {
        beanManager.get().getEvent().select(CamelEvent.class).fire(event);
    }

    @Override
    public void onComponentAdd(String name, Component component) {
        if (observedLifecycleEvents.contains(ComponentAddEvent.class.getName())) {
            fireEvent(new ComponentAddEvent(component));
        }
    }

    @Override
    public void onComponentRemove(String name, Component component) {
        if (observedLifecycleEvents.contains(ComponentRemoveEvent.class.getName())) {
            fireEvent(new ComponentRemoveEvent(component));
        }
    }

    @Override
    public void onEndpointAdd(Endpoint endpoint) {
        if (observedLifecycleEvents.contains(EndpointAddEvent.class.getName())) {
            fireEvent(new EndpointAddEvent(endpoint));
        }
    }

    @Override
    public void onEndpointRemove(Endpoint endpoint) {
        if (observedLifecycleEvents.contains(EndpointRemoveEvent.class.getName())) {
            fireEvent(new EndpointRemoveEvent(endpoint));
        }
    }

    @Override
    public void onThreadPoolAdd(CamelContext camelContext, ThreadPoolExecutor threadPool, String id,
            String sourceId, String routeId, String threadPoolProfileId) {
        if (observedLifecycleEvents.contains(ThreadPoolAddEvent.class.getName())) {
            fireEvent(new ThreadPoolAddEvent(camelContext, threadPool, id, sourceId, routeId, threadPoolProfileId));
        }
    }

    @Override
    public void onThreadPoolRemove(CamelContext camelContext, ThreadPoolExecutor threadPool) {
        if (observedLifecycleEvents.contains(ThreadPoolRemoveEvent.class.getName())) {
            fireEvent(new ThreadPoolRemoveEvent(camelContext, threadPool));
        }
    }

    @Override
    public void onServiceAdd(CamelContext context, Service service, org.apache.camel.Route route) {
        if (observedLifecycleEvents.contains(ServiceAddEvent.class.getName())) {
            fireEvent(new ServiceAddEvent(context, service, route));
        }
    }

    @Override
    public void onServiceRemove(CamelContext context, Service service, org.apache.camel.Route route) {
        if (observedLifecycleEvents.contains(ServiceAddEvent.class.getName())) {
            fireEvent(new ServiceRemoveEvent(context, service, route));
        }
    }

    @Override
    public void onRoutesAdd(Collection<Route> routes) {
        // superseded by management events
    }

    @Override
    public void onRoutesRemove(Collection<Route> routes) {
        // superseded by management events
    }

    @Override
    public void onRouteContextCreate(Route route) {
        // superseded by management events
    }

    @Override
    public void onContextInitialized(CamelContext context) throws VetoCamelContextStartException {
        // superseded by management events
    }
}
