// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.reporting.icann;

import static com.google.common.base.Throwables.getRootCause;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_REPORT_TYPES;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SHOULD_UPLOAD;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SUBDIR;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.batch.CloudTasksUtils;
import google.registry.bigquery.BigqueryJobFailureException;
import google.registry.config.RegistryConfig.Config;
import google.registry.groups.GmailClient;
import google.registry.reporting.ReportingModule;
import google.registry.reporting.icann.IcannReportingModule.ReportType;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.EmailMessage;
import google.registry.util.Retrier;
import jakarta.inject.Inject;
import jakarta.mail.internet.InternetAddress;
import java.time.Duration;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

/**
 * Action that generates monthly ICANN activity and transactions reports.
 *
 * <p>The reports are stored in GCS under gs://[project-id]-reporting/[subdir]. We also store a
 * MANIFEST.txt file that contains a list of filenames generated, to facilitate subsequent uploads.
 *
 * <p>Parameters:
 *
 * <p>yearMonth: the reporting month in yyyy-MM format. Defaults to the previous month at runtime
 * (i.e. a run on 2017-09-01 defaults to 2017-08's reports).
 *
 * <p>subdir: the subdirectory of gs://[project-id]-reporting/ to upload to. For example:
 * "manual/dir" means reports will be stored under gs://[project-id]-reporting/manual/dir. Defaults
 * to "icann/monthly/[yearMonth]".
 *
 * <p>reportTypes: the type of reports to generate. You can specify either 'activity' or
 * 'transactions'. If none specified - defaults to generating both.
 */
@Action(
    service = Action.Service.BACKEND,
    path = IcannReportingStagingAction.PATH,
    method = POST,
    auth = Auth.AUTH_ADMIN)
public final class IcannReportingStagingAction implements Runnable {

  static final String PATH = "/_dr/task/icannReportingStaging";

  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
  private static final String CRON_QUEUE = "retryable-cron-tasks";
  private static final String DEFAULT_SUBDIR = "icann/monthly";

  @Inject YearMonth yearMonth;
  @Inject @Parameter(PARAM_SUBDIR) Optional<String> overrideSubdir;
  @Inject @Parameter(PARAM_REPORT_TYPES) ImmutableSet<ReportType> reportTypes;

  @Inject
  @Parameter(ReportingModule.SEND_EMAIL)
  boolean sendEmail;

  @Inject
  @Parameter(PARAM_SHOULD_UPLOAD)
  boolean shouldUpload;

  @Inject IcannReportingStager stager;
  @Inject Retrier retrier;
  @Inject Response response;

  @Inject
  @Config("newAlertRecipientEmailAddress")
  InternetAddress recipient;

  @Inject GmailClient gmailClient;
  @Inject CloudTasksUtils cloudTasksUtils;

  @Inject IcannReportingStagingAction() {}

  @Override
  public void run() {
    try {
      String subdir = getSubdir(yearMonth);
      retrier.callWithRetry(
          () -> {
            ImmutableList.Builder<String> manifestedFilesBuilder = new ImmutableList.Builder<>();
            for (ReportType reportType : reportTypes) {
              manifestedFilesBuilder.addAll(stager.stageReports(yearMonth, subdir, reportType));
            }
            ImmutableList<String> manifestedFiles = manifestedFilesBuilder.build();
            stager.createAndUploadManifest(subdir, manifestedFiles);

            logger.atInfo().log("Completed staging %d report files.", manifestedFiles.size());
            if (sendEmail) {
              gmailClient.sendEmail(
                  EmailMessage.newBuilder()
                      .setSubject("ICANN Monthly report staging summary [SUCCESS]")
                      .setBody(
                          String.format(
                              "Completed staging the following %d ICANN reports:\n%s",
                              manifestedFiles.size(), Joiner.on('\n').join(manifestedFiles)))
                      .addRecipient(recipient)
                      .build());
            } else {
              logger.atInfo().log("Would have sent staging report summary to %s", recipient);
            }
            response.setStatus(SC_OK);
            response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
            response.setPayload("Completed staging action.");

            if (shouldUpload) {
              logger.atInfo().log("Enqueueing report upload.");
              cloudTasksUtils.enqueue(
                  CRON_QUEUE,
                  cloudTasksUtils.createTaskWithDelay(
                      IcannReportingUploadAction.class, POST, null, Duration.ofMinutes(2)));
            } else {
              logger.atInfo().log("Would have enqueued report upload");
            }
            return null;
          },
          BigqueryJobFailureException.class);
    } catch (Throwable e) {
      gmailClient.sendEmail(
          EmailMessage.create(
              "ICANN Monthly report staging summary [FAILURE]",
              String.format(
                  "Staging failed due to %s, check logs for more details.", getRootCause(e)),
              recipient));
      response.setStatus(SC_INTERNAL_SERVER_ERROR);
      response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
      response.setPayload(
          String.format(
              "Staging failed due to %s",
              getRootCause(e).toString()));
      throw new RuntimeException("Staging action failed.", e);
    }
  }

  String getSubdir(YearMonth yearMonth) {
    return IcannReportingModule.checkSubdirValid(
        overrideSubdir.orElse(
            String.format(
                "%s/%s",
                DEFAULT_SUBDIR, DateTimeFormatter.ofPattern("yyyy-MM").format(yearMonth))));
  }
}
