001/*
002 * Copyright 2025 devteam@scivicslab.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing,
011 * software distributed under the License is distributed on an
012 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied.  See the License for the
014 * specific language governing permissions and limitations
015 * under the License.
016 */
017
018package com.scivicslab.actoriac.report;
019
020import com.scivicslab.pojoactor.core.Action;
021import com.scivicslab.pojoactor.core.ActionResult;
022import com.scivicslab.pojoactor.core.JsonState;
023import com.scivicslab.pojoactor.workflow.IIActorRef;
024import com.scivicslab.pojoactor.workflow.IIActorSystem;
025
026import org.json.JSONObject;
027import org.yaml.snakeyaml.Yaml;
028
029import java.io.InputStream;
030import java.nio.file.Files;
031import java.nio.file.Path;
032import java.nio.file.Paths;
033import java.util.Map;
034import java.util.logging.Logger;
035
036/**
037 * Actor for building and outputting workflow reports.
038 *
039 * <p>This actor wraps a {@link ReportBuilder} POJO and provides workflow-callable
040 * actions via {@code @Action} annotations. It handles the integration with the
041 * actor system, database, and output multiplexer.</p>
042 *
043 * <h2>Actions:</h2>
044 * <ul>
045 *   <li>{@code addWorkflowInfo} - Add workflow metadata section</li>
046 *   <li>{@code addJsonStateSection} - Add actor's JsonState as YAML</li>
047 *   <li>{@code report} - Build and output the report</li>
048 * </ul>
049 *
050 * <p>Transition履歴の詳細表示は{@code TransitionViewerPlugin}で行う。</p>
051 *
052 * @author devteam@scivicslab.com
053 * @since 2.15.0
054 */
055public class ReportBuilderIIAR extends IIActorRef<ReportBuilder> {
056
057    private static final String CLASS_NAME = ReportBuilderIIAR.class.getName();
058    private static final Logger logger = Logger.getLogger(CLASS_NAME);
059
060    /**
061     * Constructs a new ReportBuilderIIAR with a new POJO instance.
062     *
063     * <p>Required by {@code loader.createChild} for dynamic instantiation.</p>
064     *
065     * @param actorName the actor name
066     * @param system the actor system
067     */
068    public ReportBuilderIIAR(String actorName, IIActorSystem system) {
069        super(actorName, new ReportBuilder(), system);
070        // POJOにsystemとselfRefを設定(ReportBuilder.build()が子アクターを取得するため)
071        this.object.setActorSystem(system);
072        this.object.setIIActorRef(this);
073    }
074
075    /**
076     * Constructs a new ReportBuilderIIAR.
077     *
078     * @param name the actor name
079     * @param builder the ReportBuilder POJO to wrap
080     */
081    public ReportBuilderIIAR(String name, ReportBuilder builder) {
082        super(name, builder);
083    }
084
085    /**
086     * Constructs a new ReportBuilderIIAR with actor system.
087     *
088     * @param name the actor name
089     * @param builder the ReportBuilder POJO to wrap
090     * @param system the actor system
091     */
092    public ReportBuilderIIAR(String name, ReportBuilder builder, IIActorSystem system) {
093        super(name, builder, system);
094    }
095
096    // ========================================================================
097    // Actions
098    // ========================================================================
099
100    /**
101     * Adds workflow info section.
102     *
103     * <p>Gets workflow path from nodeGroup, reads the YAML file to extract
104     * name and description, and adds a WorkflowInfoSection to the report.</p>
105     *
106     * @param args unused
107     * @return ActionResult indicating success or failure
108     */
109    @Action("addWorkflowInfo")
110    public ActionResult addWorkflowInfo(String args) {
111        logger.info("ReportBuilderIIAR.addWorkflowInfo");
112
113        String workflowPath = getWorkflowPathFromNodeGroup();
114        if (workflowPath == null) {
115            return new ActionResult(false, "Could not get workflow path from nodeGroup");
116        }
117
118        String name = null;
119        String description = null;
120
121        // Try to read workflow YAML for name and description
122        try {
123            Path path = Paths.get(workflowPath);
124            if (!Files.exists(path)) {
125                path = Paths.get(System.getProperty("user.dir"), workflowPath);
126            }
127
128            if (Files.exists(path)) {
129                try (InputStream is = Files.newInputStream(path)) {
130                    Yaml yaml = new Yaml();
131                    Map<String, Object> data = yaml.load(is);
132                    if (data != null) {
133                        name = (String) data.get("name");
134                        Object descObj = data.get("description");
135                        description = descObj != null ? descObj.toString().trim() : null;
136                    }
137                }
138            }
139        } catch (Exception e) {
140            logger.warning("ReportBuilderIIAR.addWorkflowInfo: Could not read workflow file: " + e.getMessage());
141        }
142
143        this.object.addSection(new WorkflowInfoSection(workflowPath, name, description));
144        return new ActionResult(true, "Workflow info section added");
145    }
146
147    /**
148     * Adds JsonState section for specified actor.
149     *
150     * <p>Arguments (JSON):</p>
151     * <ul>
152     *   <li>{@code actor} - Actor name (required)</li>
153     *   <li>{@code path} - JsonState path filter (optional)</li>
154     * </ul>
155     *
156     * @param args JSON arguments
157     * @return ActionResult indicating success or failure
158     */
159    @Action("addJsonStateSection")
160    public ActionResult addJsonStateSection(String args) {
161        logger.info("ReportBuilderIIAR.addJsonStateSection: args=" + args);
162
163        String actorName;
164        String path = "";
165
166        try {
167            JSONObject json = new JSONObject(args);
168            actorName = json.getString("actor");
169            path = json.optString("path", "");
170        } catch (Exception e) {
171            return new ActionResult(false, "Invalid arguments: " + e.getMessage());
172        }
173
174        if (system() == null) {
175            return new ActionResult(false, "ActorSystem not available");
176        }
177
178        IIActorRef<?> targetActor = ((IIActorSystem) system()).getIIActor(actorName);
179        if (targetActor == null) {
180            return new ActionResult(false, "Actor not found: " + actorName);
181        }
182
183        JsonState jsonState = targetActor.json();
184        if (jsonState == null) {
185            return new ActionResult(false, "Actor has no JsonState: " + actorName);
186        }
187
188        String yamlContent = jsonState.toStringOfYaml(path);
189        this.object.addSection(new JsonStateSection(actorName, yamlContent));
190
191        return new ActionResult(true, "JsonState section added for " + actorName);
192    }
193
194    /**
195     * Builds and outputs the report.
196     *
197     * @param args unused
198     * @return ActionResult with the report content
199     */
200    @Action("report")
201    public ActionResult report(String args) {
202        logger.info("ReportBuilderIIAR.report");
203
204        String reportContent = this.object.build();
205        reportToMultiplexer(reportContent);
206
207        return new ActionResult(true, reportContent);
208    }
209
210    // ========================================================================
211    // Helper Methods
212    // ========================================================================
213
214    /**
215     * Gets workflow path from nodeGroup actor.
216     */
217    private String getWorkflowPathFromNodeGroup() {
218        if (system() == null) return null;
219        IIActorSystem iiSystem = (IIActorSystem) system();
220
221        IIActorRef<?> nodeGroup = iiSystem.getIIActor("nodeGroup");
222        if (nodeGroup == null) return null;
223
224        ActionResult result = nodeGroup.callByActionName("getWorkflowPath", "");
225        return result.isSuccess() ? result.getResult() : null;
226    }
227
228    /**
229     * Outputs report to outputMultiplexer.
230     */
231    private void reportToMultiplexer(String data) {
232        if (system() == null) return;
233        IIActorSystem iiSystem = (IIActorSystem) system();
234
235        IIActorRef<?> multiplexer = iiSystem.getIIActor("outputMultiplexer");
236        if (multiplexer == null) return;
237
238        JSONObject arg = new JSONObject();
239        arg.put("source", "report-builder");
240        arg.put("type", "plugin-result");
241        arg.put("data", data);
242        multiplexer.callByActionName("add", arg.toString());
243    }
244}