/*
 * Copyright 2013-2026, Seqera Labs
 *
 * Licensed 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 nextflow.executor

import java.nio.file.Path

import groovy.transform.CompileStatic
import nextflow.processor.TaskRun
/**
 * Execute a task script by running it on the NQSII cluster
 *
 * Read more
 *   https://www.rz.uni-kiel.de/en/our-portfolio/hiperf/nec-linux-cluster
 *   https://wickie.hlrs.de/platforms/index.php/Batch_system
 *
 * @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
 * @author Till Bayer <till.bayer@gmail.com>
 */
@CompileStatic
class NqsiiExecutor extends AbstractGridExecutor {

    /**
     * Gets the directives to submit the specified task to the cluster for execution
     *
     * @param task A {@link TaskRun} to be submitted
     * @param result The {@link List} instance to which add the job directives
     * @return A {@link List} containing all directive tokens and values.
     */
    protected List<String> getDirectives(TaskRun task, List<String> result) {

        result << '-N' << getJobNameFor(task)
        result << '-o' << quote(task.workDir.resolve(TaskRun.CMD_LOG))
        result << '-j' << 'o'  // stderr to stdin
        result << '-b' << '1'    // set number of nodes to 1

        // the requested queue name
        if( task.config.queue ) {
            result << '-q' << (task.config.queue as String)
        }

        //number of cpus for multiprocessing/multi-threading
        if( task.config.getCpus()>1 ) {
            result << "-l" << "cpunum_job=${task.config.getCpus()}".toString()
        } else {
            result << "-l" << "cpunum_job=1"
        }

        // max task duration
        if( task.config.getTime() ) {
            final time = task.config.getTime()
            result << "-l" << "elapstim_req=${time.format('HH:mm:ss')}".toString()
        }

        // task max memory
        if( task.config.getMemory() ) {
            result << "-l" << "memsz_job=${task.config.getMemory().toString().replaceAll(/[\s]/,'').toLowerCase()}".toString()
        }

        // -- at the end append the command script wrapped file name
        addClusterOptionsDirective(task.config, result)

        return result
    }


    /*
     * Prepare the 'qsub' cmdline
     */
    List<String> getSubmitCommandLine(TaskRun task, Path scriptFile ) {
        return ['qsub', scriptFile.name]
    }

    protected String getHeaderToken() { '#PBS' }

    String sanitizeJobName( String name ) {
        // NQSII does not allow more than 63 characters for the job name string
        name.size()>63 ? name.substring(0,63) : name
    }

    /**
     * Parse the string returned by the {@code qsub} command and extract the job ID string
     *
     * @param text The string returned when submitting the job
     * @return The actual job ID string
     */
    @Override
    def parseJobId( String text ) {
        def pattern = ~/Request (\d+).+ submitted to queue.+/
        for( String line : text.readLines() ) {
            def m = pattern.matcher(line)
            if( m.find() ) {
                return m.group(1)
            }
        }

        throw new IllegalStateException("Invalid NQSII submit response:\n$text\n\n")
    }

    @Override
    protected List<String> getKillCommand() { ['qdel'] }

    @Override
    protected List<String> queueStatusCommand(Object queue) {
        def result = ['qstat']
        if( queue )
            result << '-q' << queue.toString()

        return result
    }

    static protected Map<String,QueueStatus> DECODE_STATUS = [
            'PRR': QueueStatus.RUNNING,
            'RUN': QueueStatus.RUNNING,
            'STG': QueueStatus.RUNNING,
            'CHK': QueueStatus.RUNNING,
            'EXT': QueueStatus.RUNNING,
            'POR': QueueStatus.RUNNING,
            'ARI': QueueStatus.PENDING,
            'FWD': QueueStatus.PENDING,
            'MIG': QueueStatus.PENDING,
            'QUE': QueueStatus.PENDING,
            'WAT': QueueStatus.PENDING,
            'GQD': QueueStatus.PENDING,
            'SUS': QueueStatus.HOLD,
            'HLD': QueueStatus.HOLD,
            'HOL': QueueStatus.HOLD
    ]

    @Override
    protected Map<String, QueueStatus> parseQueueStatus(String text) {

        final result = new LinkedHashMap<String, QueueStatus>()
        text?.eachLine{ String row, int index ->
            if( index< 2 ) return
            def cols = row.trim().split(/\s+/)
            if( cols.size()>6 ) {
            def id = cols[0].tokenize('.')
                result.put( id[0], DECODE_STATUS[cols[5]] )
            }
        }

        return result
    }

}
