/*
 *  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.causeway.extensions.commandreplay.primary.ui;

import java.util.List;
import java.util.UUID;

import jakarta.inject.Inject;
import jakarta.inject.Named;

import org.springframework.context.annotation.Profile;
import org.jspecify.annotations.Nullable;

import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.annotation.ActionLayout;
import org.apache.causeway.applib.annotation.DomainService;
import org.apache.causeway.applib.annotation.DomainServiceLayout;
import org.apache.causeway.applib.annotation.MemberSupport;
import org.apache.causeway.applib.annotation.ParameterLayout;
import org.apache.causeway.applib.annotation.PriorityPrecedence;
import org.apache.causeway.applib.annotation.Publishing;
import org.apache.causeway.applib.annotation.SemanticsOf;
import org.apache.causeway.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
import org.apache.causeway.applib.services.jaxb.JaxbService;
import org.apache.causeway.applib.services.message.MessageService;
import org.apache.causeway.applib.value.Clob;
import org.apache.causeway.extensions.commandlog.applib.CausewayModuleExtCommandLogApplib;
import org.apache.causeway.extensions.commandlog.applib.dom.CommandLogEntry;
import org.apache.causeway.extensions.commandlog.applib.dom.CommandLogEntryRepository;
import org.apache.causeway.extensions.commandlog.applib.dom.CommandLogEntryRepository.NotFoundException;
import org.apache.causeway.extensions.commandreplay.primary.CausewayModuleExtCommandReplayPrimary;
import org.apache.causeway.schema.cmd.v2.CommandDto;
import org.apache.causeway.schema.cmd.v2.CommandsDto;

import lombok.RequiredArgsConstructor;

/**
 * @since 2.0 {@index}
 */
@DomainService
@DomainServiceLayout(
    named = "Activity",
    menuBar = DomainServiceLayout.MenuBar.SECONDARY
)
@Named(CausewayModuleExtCommandReplayPrimary.NAMESPACE + ".CommandReplayOnPrimaryService")
@jakarta.annotation.Priority(PriorityPrecedence.EARLY)
@Profile("commandreplay-primary")
@RequiredArgsConstructor
//@Slf4j
public class CommandReplayOnPrimaryService {

    @Inject final CommandLogEntryRepository commandLogEntryRepository;
    @Inject final JaxbService jaxbService;
    @Inject final MessageService messageService;
    @Inject final ContentMappingServiceForCommandsDto contentMappingServiceForCommandsDto;

    public static abstract class ActionDomainEvent<T>
    extends CausewayModuleExtCommandLogApplib.ActionDomainEvent<T> { }

    @Action(
            commandPublishing = Publishing.DISABLED,
            domainEvent = findCommands.ActionDomainEvent.class,
            executionPublishing = Publishing.DISABLED,
            semantics = SemanticsOf.SAFE
    )
    @ActionLayout(cssClassFa = "fa-search", sequence="40")
    public class findCommands{

        public class ActionDomainEvent extends CommandReplayOnPrimaryService.ActionDomainEvent<findCommands> { }

        /**
         * These actions should be called with HTTP Accept Header set to:
         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.causeway.schema.cmd.v2.CommandsDto"</code>
         *
         * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
         * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
         * @throws NotFoundException - if the command with specified transaction cannot be found.
         */
        @MemberSupport public List<? extends CommandLogEntry> act(
                @Nullable
                @ParameterLayout(named="Interaction Id")
                final UUID interactionId,
                @Nullable
                @ParameterLayout(named="Batch size")
                final Integer batchSize)
                throws NotFoundException {
            return commandLogEntryRepository.findCommandsOnPrimaryElseFail(interactionId, batchSize);
        }
        @MemberSupport public Integer default1Act() {
            return 25;
        }

    }

    @Action(
            commandPublishing = Publishing.DISABLED,
            domainEvent = downloadCommands.ActionDomainEvent.class,
            executionPublishing = Publishing.DISABLED,
            semantics = SemanticsOf.SAFE
    )
    @ActionLayout(cssClassFa = "fa-download", sequence="50")
    public class downloadCommands {

        public class ActionDomainEvent extends CommandReplayOnPrimaryService.ActionDomainEvent<downloadCommands> { }

        /**
         * These actions should be called with HTTP Accept Header set to:
         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.causeway.schema.cmd.v2.CommandsDto"</code>
         *
         * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
         * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
         * @throws NotFoundException - if the command with specified transaction cannot be found.
         */
        @MemberSupport public Clob act(
                @Nullable
                final UUID interactionId,
                @Nullable
                final Integer batchSize,
                final String filenamePrefix) {
            final List<? extends CommandLogEntry> commands = commandLogEntryRepository.findSince(interactionId, batchSize);
            if(commands == null) {
                messageService.informUser("No commands found");
            }

            final CommandsDto commandsDto =
                    contentMappingServiceForCommandsDto.map(commands);

            final String fileName = String.format(
                    "%s_%s.xml", filenamePrefix, elseDefault(interactionId));

            final String xml = jaxbService.toXml(commandsDto);
            return new Clob(fileName, "application/xml", xml);
        }
        @MemberSupport public Integer default1Act() {
            return 25;
        }
        @MemberSupport public String default2Act() {
            return "commands_from";
        }
    }

    @Action(
            commandPublishing = Publishing.DISABLED,
            domainEvent = downloadCommandById.ActionDomainEvent.class,
            executionPublishing = Publishing.DISABLED,
            semantics = SemanticsOf.SAFE
    )
    @ActionLayout(cssClassFa = "fa-download", sequence="50")
    public class downloadCommandById {

        public class ActionDomainEvent extends CommandReplayOnPrimaryService.ActionDomainEvent<downloadCommandById> { }

        /**
         * This action should be called with HTTP Accept Header set to:
         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.causeway.schema.cmd.v2.CommandDto"</code>
         *
         * @param interactionId - to download.
         * @throws NotFoundException - if the command with specified transaction cannot be found.
         */
        @MemberSupport public Clob act(
                final UUID interactionId,
                final String filenamePrefix) {

            return commandLogEntryRepository.findByInteractionId(interactionId)
                    .map(commandJdo -> {

                        final CommandDto commandDto = commandJdo.getCommandDto();

                        final String fileName = String.format(
                                "%s_%s.xml", filenamePrefix, elseDefault(interactionId));

                        final String xml = jaxbService.toXml(commandDto);
                        return new Clob(fileName, "application/xml", xml);

                    }).orElseGet(() -> {
                        messageService.informUser("No command found");
                        return null;
                    });
        }
        @MemberSupport public String default1Act() {
            return "command";
        }

    }

    private static String elseDefault(final UUID uuid) {
        return uuid != null ? uuid.toString() : "00000000-0000-0000-0000-000000000000";
    }

}
