package com.capgemini.reports.execution.strategies;

import com.capgemini.framework.common.DateHelper;
import com.capgemini.framework.common.ReportFolderOutputHelper;
import com.capgemini.framework.common.SettingsHelper;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.ReportLaunchTypeEnum;
import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.DbReport;
import com.capgemini.reports.database.javabean.DbReportOwner;
import com.capgemini.reports.database.manager.MngReport;
import com.capgemini.reports.database.manager.MngReportOwner;
import com.capgemini.reports.exceptions.NotFoundException;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.execution.actions.*;
import com.capgemini.reports.printing.PrintParameters;
import com.google.common.io.Files;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * Class representing the strategy to execute for scheduled reports.
 */
@Slf4j
@EqualsAndHashCode(callSuper = true)
public class ScheduledExecutionStrategy extends AbstractExecutionStrategy {
  private static final long serialVersionUID = -1874201095083131827L;
  /**
   * Location in HD of the folder where the latest version of scheduled reports are kept.
   */
  private static final String SCHEDULING_LOCATION_OUTPUT = SettingsHelper.getSchedulingLocation();

  /**
   * PRepares an execution strategy for scheduled reports.
   */
  public ScheduledExecutionStrategy() {
    super(new CompositePreAction(new UpdateReportAction(), new QuickCheckAction(), new UpdateReportInDbAction(), new LoggingAction()),
      new CompositePostAction(new UpdateReportAction(), new UpdateReportInDbAction(), new ScheduledEmailAction(), new LoggingAction()), makeSchedulingDefaultErrorAction());
  }


  private static IErrorExecutionStep makeSchedulingDefaultErrorAction() {
    UpdateReportAction updateReportAction = new UpdateReportAction();
    UpdateReportInDbAction updateReportInDbAction = new UpdateReportInDbAction();
    ScheduledEmailAction scheduledEmailAction = new ScheduledEmailAction();
    LoggingAction loggingAction = new LoggingAction();
    return new CompositeErrorAction(updateReportAction, updateReportInDbAction, scheduledEmailAction, loggingAction);
  }

  @Override
  public final ReportLaunchTypeEnum getLaunchType() {
    return ReportLaunchTypeEnum.SCHEDULED;
  }


  @Override
  public File print(final PrintParameters printParameters, final AbstractExecutionReport<?, ?> report) throws KurtException {
    String fileNameShort = fileNameShort(report) + printParameters.getOuputFileExtension();
    if (ReportFolderOutputHelper.checkIfAlreadyLaunched(report.getSchedulingId())) {
      // It has already launched so we have to delete the precedent file in the scheduled output folder
      // But the history folder doesn't change
      try {
        ReportFolderOutputHelper.deleteScheduledReportFile(fileNameShort);
      }
      catch (NotFoundException e) {
        log.warn("Unable to delete file generated by previous scheduled execution. Continuing scheduled job.", e);
      }
    }

    File original = printReport(printParameters, report);
    File scheduledFile = new File(SCHEDULING_LOCATION_OUTPUT + fileNameShort);
    try {
      Files.move(original, scheduledFile); // This RENAMES the file
      log.debug("Renamed scheduling file [{}] to [{}]", original.getAbsoluteFile(), scheduledFile.getAbsolutePath());
    }
    catch (IOException e) {
      File originalFileName = original.getAbsoluteFile();
      String destinationFileName = scheduledFile.getAbsolutePath();
      throw new KurtException("Unable to rename file " + originalFileName + " to " + destinationFileName, e);
    }

    // Add the scheduled report in the historic folder
    String reportFileNameHistory = getReportFileName(report) + printParameters.getOuputFileExtension();
    try {
      ReportFolderOutputHelper.copyToHistoryFolder(scheduledFile, reportFileNameHistory);
      log.debug("Saved scheduling file [{}] to history [{}]", scheduledFile.getAbsoluteFile(), reportFileNameHistory);
    }
    catch (IOException e) {
      File originalFileName = scheduledFile.getAbsoluteFile();
      throw new KurtException("Unable to copy file " + originalFileName + " to " + reportFileNameHistory, e);
    }

    // Now update DB
    MngReport mng = new MngReport();
    mng.updateFileName(report.getReportTechnicalUid(), reportFileNameHistory);
    log.debug("Registering report {} file name {} to DB ", report.getReportTechnicalUid(), reportFileNameHistory);
    return scheduledFile;
  }

  @Override
  public String getReportFileName(final AbstractExecutionReport<?, ?> report) {
    // Give the name : reportcode+"-"+schedulingname+"-"+postingDate
    Timestamp date;
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      MngReport mngReport = new MngReport();
      DbReport dbReport = mngReport.searchByUid(report.getReportTechnicalUid(), session);
      List<DbReportOwner> owners = new MngReportOwner().searchReportOwners(session, dbReport.getId());
      if (owners.isEmpty()) {
        date = DateHelper.getCurrentSqlTimestamp();
        log.error("No owners of scheduled report, using default scheduling name for file name");
      }
      else {
        date = owners.get(0).getRequestDate();
      }
    }
    catch (NotFoundException e) {
      date = DateHelper.getCurrentSqlTimestamp();
      log.error("Report not found in DB, using default scheduling name for file name", e);
    }
    finally {
      session.close();
    }
    String dateStr = new SimpleDateFormat("yyyyMMdd_HHmmss").format(date);
    String fileNameShort = fileNameShort(report);
    return fileNameShort + "-" + dateStr;
  }

  private String fileNameShort(final AbstractExecutionReport<?, ?> report) {
    String reportCode = report.getReportIdentifier().getReportCode();
    String schedulingName = report.getSchedulingName();
    return reportCode + "-" + schedulingName;
  }
}
