// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC 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.openqa.selenium.grid.commands;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.internal.DefaultConsole;
import com.google.auto.service.AutoService;
import com.google.common.io.Resources;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Set;
import org.openqa.selenium.cli.CliCommand;
import org.openqa.selenium.cli.WrappedPrintWriter;
import org.openqa.selenium.grid.config.Role;
import org.openqa.selenium.grid.server.HelpFlags;

@AutoService(CliCommand.class)
public class InfoCommand implements CliCommand {

  public String getName() {
    return "info";
  }

  public String getDescription() {
    return "Prints information for commands and topics.";
  }

  @Override
  public Set<Role> getConfigurableRoles() {
    return Collections.singleton(Role.of("info"));
  }

  @Override
  public Set<Object> getFlagObjects() {
    return Collections.emptySet();
  }

  public Executable configure(PrintStream out, PrintStream err, String... args) {
    HelpFlags help = new HelpFlags();
    InfoFlags topic = new InfoFlags();

    JCommander commander =
        JCommander.newBuilder().programName("selenium").addObject(help).addObject(topic).build();
    commander.setConsole(new DefaultConsole(out));

    return () -> {
      try {
        commander.parse(args);
      } catch (ParameterException e) {
        err.println(e.getMessage());
        commander.usage();
        return;
      }

      if (help.displayHelp(commander, out)) {
        return;
      }

      String toDisplay;
      String title;
      switch (topic.topic) {
        case "config":
          title = "Configuring Selenium";
          toDisplay = "config.txt";
          break;

        case "security":
          title = "About Security";
          toDisplay = "security.txt";
          break;

        case "tracing":
          title = "About Tracing";
          toDisplay = "tracing.txt";
          break;

        case "sessionmap":
          title = "About SessionMaps";
          toDisplay = "sessionmaps.txt";
          break;

        case "help":
        case "info":
        default:
          title = "Info";
          toDisplay = "info.txt";
          break;
      }

      String path = getClass().getPackage().getName().replaceAll("\\.", "/") + "/" + toDisplay;
      String content;
      try {
        content = readContent(path);
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }

      try (PrintWriter outWriter = new WrappedPrintWriter(out, 72, 0)) {
        outWriter.printf(
            "%n%s%n%s%n%n", title, String.join("", Collections.nCopies(title.length(), "=")));
        outWriter.print(content);
        outWriter.println("\n\n");
      }
    };
  }

  private String readContent(String path) throws IOException {
    String unformattedText =
        Resources.toString(Resources.getResource(path), StandardCharsets.UTF_8);
    StringBuilder formattedText = new StringBuilder();
    try (BufferedReader reader = new BufferedReader(new StringReader(unformattedText))) {
      boolean inCode = false;

      for (String line = reader.readLine(); line != null; line = reader.readLine()) {
        if (line.isEmpty()) {
          if (inCode) {
            formattedText.append("\n");
          } else {
            formattedText.append("\n\n");
          }
        } else if ("```".equals(line)) {
          inCode = !inCode;
        } else {
          if (line.startsWith("=")) {
            formattedText.append("\n");
          }
          formattedText.append(line);
          if (inCode || line.matches("^\\s*\\*.*") || line.matches("^\\s*\\d+\\..*")) {
            formattedText.append("\n");
          } else {
            formattedText.append(" ");
          }
        }
      }
    }

    return formattedText.toString();
  }
}
