/*-
 * ~~~~~~licensing~~~~~~
 * test-producers
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.core.producer.shell;

import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.Option;
import org.apache.karaf.shell.api.action.lifecycle.Reference;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.apache.karaf.shell.support.table.ShellTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonObject;

import ru.entaxy.platform.base.objects.EntaxyObject;
import ru.entaxy.platform.base.objects.EntaxyObjectService;
import ru.entaxy.platform.base.objects.shell.completers.ObjectCompleter;
import ru.entaxy.platform.base.support.CommonUtils;
import ru.entaxy.platform.base.support.JSONUtils;
import ru.entaxy.platform.core.producer.api.ExecutionPlan;
import ru.entaxy.platform.core.producer.api.ProducerResult;
import ru.entaxy.platform.core.producer.api.ProducerResult.CommandResult;

@Service
@Command(scope = EntaxyProducerServiceSupport.SCOPE, name = "produce")
public class Produce extends EntaxyProducerServiceSupport implements Action {

    private static final Logger log = LoggerFactory.getLogger(Produce.class);

    @Argument(index = 0, name = "configuration", multiValued = false, required = false)
    String configuration;

    @Option(name = "-u", aliases = {"--config-url"}, multiValued = false, required = false)
    String configurationUrl;

    @Option(name = "-o", aliases = {"--for-object"}, multiValued = false, required = false)
    @Completion(value = ObjectCompleter.class)
    String objectId;

    @Option(name = "-t", aliases = {"--output-type"}, multiValued = false, required = false)
    String outputType;


    @Option(name = "-i", aliases = {"--build-instructions"}, multiValued = false, required = false)
    String instructions;

    @Option(name = "-p", aliases = {"--print-plan"}, multiValued = false, required = false)
    boolean printPlan;

    @Reference
    EntaxyObjectService objectService;

    @Override
    public Object execute() throws Exception {
        JsonObject configurationObject = null;
        if (CommonUtils.isValid(configuration)) {
            try {
                configurationObject = JSONUtils.getJsonRootObject(configuration);
            } catch (Exception e) {
                System.out.println("Configuration loading failed:");
                System.out.println(e.getMessage());
                return null;
            }
        } else {
            if (CommonUtils.isValid(configurationUrl)) {
                String urlToOpen = configurationUrl;

                if (!urlToOpen.contains(";//")) {
                    // suppose it's relative file path
                    Path karafHome = Paths.get(System.getProperty("karaf.home"));
                    Path targetPath = karafHome.toAbsolutePath().resolve(Paths.get(urlToOpen));

                    urlToOpen = targetPath.toUri().toURL().toString();

                }

                try {
                    URL url = new URL(urlToOpen);
                    configurationObject = JSONUtils.getJsonRootObject(url);
                } catch (Exception e) {
                    System.out.println(String.format("Configuration loading failed: %s", e.getClass().getName()));
                    System.out.println(String.format("  -> %s", e.getMessage()));
                    return null;
                }
            } else {
                if (CommonUtils.isValid(objectId)) {
                    List<EntaxyObject> candidates = objectService.getObjects().stream()
                            .filter(obj -> obj.getObjectFullId().equals(objectId)).collect(Collectors.toList());
                    if (candidates.size() == 0) {
                        System.out.println("Object not found: " + objectId);
                        return null;
                    }
                    configurationObject = JSONUtils.getJsonRootObject(candidates.get(0).getConfiguration());
                    if (CommonUtils.isValid(outputType))
                        configurationObject.addProperty("outputType", outputType);
                } else {
                    System.out.println("Configuration not found");
                    return null;
                }
            }
        }

        if (!CommonUtils.isValid(instructions))
            this.instructions = "{}";

        ProducerResult result = producerService.produce(configurationObject, instructions);

        if (printPlan) {
            ExecutionPlan plan = result.findResultObject(ExecutionPlan.class);
            if (plan == null) {
                System.out.println("\n\tPlan not found");
            } else {
                System.out.println("\n\tPlan: "
                        + plan.tasks.stream().map(t -> t.commandId).collect(Collectors.joining(" -> ")));
            }
            System.out.println("-----------");
        }

        System.out.println("\tExecution result:");

        ShellTable table = new ShellTable();
        table.column("Command");
        table.column("Result");
        table.column("Result object class");
        table.column("Exceptions");

        for (CommandResult cr : result.getCommandResults())
            table.addRow().addContent(
                    cr.commandId,
                    cr.skipped ? "SKIPPED" : cr.result,
                    cr.resultObject == null ? "null" : cr.resultObject.getClass().getName(),
                    cr.exceptions.stream().map(e -> e.getClass().getName()).collect(Collectors.joining(",")));

        table.print(System.out);

        return null;
    }

}
