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.sections.basic;
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 java.util.logging.Logger;
027
028/**
029 * IIAR wrapper for {@link JsonStateSection}.
030 *
031 * <p>Exposes the POJO's methods as actions via {@code @Action} annotations.
032 * Retrieves JsonState from a target actor and formats it as YAML.</p>
033 *
034 * <h2>Usage in workflow YAML:</h2>
035 *
036 * <p>The actor name encodes the target actor using ":" separator:</p>
037 * <pre>{@code
038 * - actor: loader
039 *   method: createChild
040 *   arguments: ["reportBuilder", "state:nodeGroup", "...JsonStateSectionIIAR"]
041 * }</pre>
042 *
043 * <p>This creates a section that outputs nodeGroup's JsonState.</p>
044 *
045 * <p>Optional JSON path can be specified after another ":":</p>
046 * <pre>{@code
047 * - actor: loader
048 *   method: createChild
049 *   arguments: ["reportBuilder", "state:nodeGroup:cluster.nodes", "...JsonStateSectionIIAR"]
050 * }</pre>
051 *
052 * <p>This outputs only the "cluster.nodes" path from nodeGroup's JsonState.</p>
053 *
054 * @author devteam@scivicslab.com
055 * @since 2.16.0
056 */
057public class JsonStateSectionIIAR extends IIActorRef<JsonStateSection> {
058
059    private static final Logger logger = Logger.getLogger(JsonStateSectionIIAR.class.getName());
060
061    private String targetActorName;
062    private String jsonPath;
063
064    /**
065     * Constructs the IIAR with a new POJO instance.
066     *
067     * <p>The actor name is parsed to extract target actor and optional path:</p>
068     * <ul>
069     *   <li>"state:nodeGroup" → target=nodeGroup, path=null</li>
070     *   <li>"state:nodeGroup:cluster.nodes" → target=nodeGroup, path=cluster.nodes</li>
071     *   <li>"myState" → target=myState, path=null (no ":" prefix)</li>
072     * </ul>
073     *
074     * @param actorName the actor name (may encode target actor)
075     * @param system the actor system
076     */
077    public JsonStateSectionIIAR(String actorName, IIActorSystem system) {
078        super(actorName, new JsonStateSection(), system);
079        parseActorName(actorName);
080    }
081
082    /**
083     * Parses the actor name to extract target actor and optional path.
084     */
085    private void parseActorName(String actorName) {
086        if (actorName == null) {
087            return;
088        }
089
090        // Format: "prefix:targetActor" or "prefix:targetActor:jsonPath"
091        String[] parts = actorName.split(":", 3);
092        if (parts.length >= 2) {
093            // Has ":" separator - second part is target actor
094            this.targetActorName = parts[1];
095            if (parts.length >= 3) {
096                this.jsonPath = parts[2];
097            }
098        } else {
099            // No ":" - use the whole name as target
100            this.targetActorName = actorName;
101        }
102
103        object.setActorName(this.targetActorName);
104        object.setJsonPath(this.jsonPath);
105    }
106
107    /**
108     * Refreshes the YAML content from the target actor's JsonState.
109     *
110     * <p>Called automatically before generate() to ensure fresh data.</p>
111     */
112    private void refreshContent() {
113        if (targetActorName == null || actorSystem == null) {
114            return;
115        }
116
117        if (!(actorSystem instanceof IIActorSystem)) {
118            return;
119        }
120        IIActorSystem iiSystem = (IIActorSystem) actorSystem;
121
122        IIActorRef<?> targetActor = iiSystem.getIIActor(targetActorName);
123        if (targetActor == null) {
124            logger.warning("JsonStateSectionIIAR: target actor not found: " + targetActorName);
125            return;
126        }
127
128        JsonState jsonState = targetActor.json();
129        if (jsonState == null) {
130            logger.warning("JsonStateSectionIIAR: target actor has no JsonState: " + targetActorName);
131            return;
132        }
133
134        String yamlContent = jsonState.toStringOfYaml(jsonPath);
135        object.setYamlContent(yamlContent);
136    }
137
138    // ========================================================================
139    // Actions - expose POJO methods
140    // ========================================================================
141
142    @Action("generate")
143    public ActionResult generate(String args) {
144        // Refresh content before generating
145        refreshContent();
146        String content = object.generate();
147        return new ActionResult(true, content);
148    }
149
150    @Action("getTitle")
151    public ActionResult getTitle(String args) {
152        String title = object.getTitle();
153        return new ActionResult(true, title != null ? title : "");
154    }
155
156    /**
157     * Sets the target actor name dynamically.
158     *
159     * @param args the target actor name
160     * @return action result
161     */
162    @Action("setTargetActor")
163    public ActionResult setTargetActor(String args) {
164        if (args != null && !args.isEmpty()) {
165            this.targetActorName = args.trim();
166            object.setActorName(this.targetActorName);
167        }
168        return new ActionResult(true, "Target actor set: " + targetActorName);
169    }
170
171    /**
172     * Sets the JSON path filter dynamically.
173     *
174     * @param args the JSON path
175     * @return action result
176     */
177    @Action("setJsonPath")
178    public ActionResult setJsonPath(String args) {
179        this.jsonPath = (args != null && !args.isEmpty()) ? args.trim() : null;
180        object.setJsonPath(this.jsonPath);
181        return new ActionResult(true, "JSON path set: " + jsonPath);
182    }
183}