/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2019 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.axelor.apps.hr.web.extra.hours;

import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.ImportConfiguration;
import com.axelor.apps.base.db.Wizard;
import com.axelor.apps.base.db.repo.ImportConfigurationRepository;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.message.MessageServiceBaseImpl;
import com.axelor.apps.base.web.AppBaseController;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.ExtraHours;
import com.axelor.apps.hr.db.repo.ExtraHoursRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.HRMenuTagService;
import com.axelor.apps.hr.service.HRMenuValidateService;
import com.axelor.apps.hr.service.extra.hours.ExtraHoursService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.csv.script.ImportExtraHoursConfiguration;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wslite.json.JSONArray;
import wslite.json.JSONException;
import wslite.json.JSONObject;

@Singleton
public class ExtraHoursController {

  private final AppSettings appSettings = AppSettings.get();
  private final String BASE_URL = appSettings.get("portail.api.baseurl");

  private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  public void editExtraHours(ActionRequest request, ActionResponse response) {
    List<ExtraHours> extraHoursList =
        Beans.get(ExtraHoursRepository.class)
            .all()
            .filter(
                "self.user = ?1 AND self.company = ?2 AND self.statusSelect = 1",
                AuthUtils.getUser(),
                AuthUtils.getUser().getActiveCompany())
            .fetch();
    if (extraHoursList.isEmpty()) {
      response.setView(
          ActionView.define(I18n.get("Extra Hours"))
              .model(ExtraHours.class.getName())
              .add("form", "extra-hours-form")
              .map());
    } else if (extraHoursList.size() == 1) {
      response.setView(
          ActionView.define(I18n.get("ExtraHours"))
              .model(ExtraHours.class.getName())
              .add("form", "extra-hours-form")
              .param("forceEdit", "true")
              .context("_showRecord", String.valueOf(extraHoursList.get(0).getId()))
              .map());
    } else {
      response.setView(
          ActionView.define(I18n.get("ExtraHours"))
              .model(Wizard.class.getName())
              .add("form", "popup-extra-hours-form")
              .param("forceEdit", "true")
              .param("popup", "true")
              .param("show-toolbar", "false")
              .param("show-confirm", "false")
              .param("forceEdit", "true")
              .param("popup-save", "false")
              .map());
    }
  }

  public void validateExtraHours(ActionRequest request, ActionResponse response)
      throws AxelorException {

    User user = AuthUtils.getUser();
    Employee employee = user.getEmployee();

    ActionViewBuilder actionView =
        ActionView.define(I18n.get("Extra hours to Validate"))
            .model(ExtraHours.class.getName())
            .add("grid", "extra-hours-validate-grid")
            .add("form", "extra-hours-form");

    Beans.get(HRMenuValidateService.class).createValidateDomain(user, employee, actionView);

    response.setView(actionView.map());
  }

  public void editExtraHoursSelected(ActionRequest request, ActionResponse response) {
    Map extraHoursMap = (Map) request.getContext().get("extraHoursSelect");
    ExtraHours extraHours =
        Beans.get(ExtraHoursRepository.class).find(new Long((Integer) extraHoursMap.get("id")));
    response.setView(
        ActionView.define("Extra hours")
            .model(ExtraHours.class.getName())
            .add("form", "extra-hours-form")
            .param("forceEdit", "true")
            .domain("self.id = " + extraHoursMap.get("id"))
            .context("_showRecord", String.valueOf(extraHours.getId()))
            .map());
  }

  public void historicExtraHours(ActionRequest request, ActionResponse response) {

    User user = AuthUtils.getUser();
    Employee employee = user.getEmployee();

    ActionViewBuilder actionView =
        ActionView.define(I18n.get("Historic colleague extra hours"))
            .model(ExtraHours.class.getName())
            .add("grid", "extra-hours-grid")
            .add("form", "extra-hours-form");

    actionView
        .domain(
            "self.company = :_activeCompany AND (self.statusSelect = 3 OR self.statusSelect = 4)")
        .context("_activeCompany", user.getActiveCompany());

    if (employee == null || !employee.getHrManager()) {
      actionView
          .domain(actionView.get().getDomain() + " AND self.user.employee.managerUser = :_user")
          .context("_user", user);
    }

    response.setView(actionView.map());
  }

  public void showSubordinateExtraHours(ActionRequest request, ActionResponse response) {

    User user = AuthUtils.getUser();
    Company activeCompany = user.getActiveCompany();

    ActionViewBuilder actionView =
        ActionView.define(I18n.get("Extra hours to be Validated by your subordinates"))
            .model(ExtraHours.class.getName())
            .add("grid", "extra-hours-grid")
            .add("form", "extra-hours-form");

    String domain =
        "self.user.employee.managerUser.employee.managerUser = :_user AND self.company = :_activeCompany AND self.statusSelect = 2";

    long nbExtraHours =
        Query.of(ExtraHours.class)
            .filter(domain)
            .bind("_user", user)
            .bind("_activeCompany", activeCompany)
            .count();

    if (nbExtraHours == 0) {
      response.setNotify(I18n.get("No extra hours to be validated by your subordinates"));
    } else {
      response.setView(
          actionView
              .domain(domain)
              .context("_user", user)
              .context("_activeCompany", activeCompany)
              .map());
    }
  }

  /* Count Tags displayed on the menu items */

  public String extraHoursValidateMenuTag() {

    return Beans.get(HRMenuTagService.class)
        .countRecordsTag(ExtraHours.class, ExtraHoursRepository.STATUS_CONFIRMED);
  }

  // confirming request and sending mail to manager
  public void confirm(ActionRequest request, ActionResponse response) throws AxelorException {

    try {
      ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
      extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
      Beans.get(ExtraHoursService.class).confirm(extraHours);

      Message message = Beans.get(ExtraHoursService.class).sendConfirmationEmail(extraHours);
      if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
        response.setFlash(
            String.format(
                I18n.get("Email sent to %s"),
                Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
      }

    } catch (Exception e) {
      TraceBackService.trace(response, e);
    } finally {
      response.setReload(true);
    }
  }

  /**
   * validating request and sending mail to applicant
   *
   * @param request
   * @param response
   * @throws AxelorException
   */
  public void valid(ActionRequest request, ActionResponse response) throws AxelorException {

    try {
      ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
      extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
      Beans.get(ExtraHoursService.class).validate(extraHours);

      Message message = Beans.get(ExtraHoursService.class).sendValidationEmail(extraHours);
      if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
        response.setFlash(
            String.format(
                I18n.get("Email sent to %s"),
                Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
      }
      Beans.get(PeriodService.class)
          .checkPeriod(extraHours.getCompany(), extraHours.getValidationDate());

    } catch (Exception e) {
      TraceBackService.trace(response, e);
    } finally {
      response.setReload(true);
    }
  }

  // refusing request and sending mail to applicant
  public void refuse(ActionRequest request, ActionResponse response) throws AxelorException {

    try {
      ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
      extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
      Beans.get(ExtraHoursService.class).refuse(extraHours);

      Message message = Beans.get(ExtraHoursService.class).sendRefusalEmail(extraHours);
      if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
        response.setFlash(
            String.format(
                I18n.get("Email sent to %s"),
                Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
      }

    } catch (Exception e) {
      TraceBackService.trace(response, e);
    } finally {
      response.setReload(true);
    }
  }

  // canceling request and sending mail to applicant
  public void cancel(ActionRequest request, ActionResponse response) throws AxelorException {
    try {
      ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
      extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
      Beans.get(ExtraHoursService.class).cancel(extraHours);

      Message message = Beans.get(ExtraHoursService.class).sendCancellationEmail(extraHours);
      if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
        response.setFlash(
            String.format(
                I18n.get("Email sent to %s"),
                Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
      }
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    } finally {
      response.setReload(true);
    }
  }

  // counting total hours while computing extra hours lines
  public void compute(ActionRequest request, ActionResponse response) {
    try {
      ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
      // Beans.get(ExtraHoursService.class).compute(extraHours);
      response.setValue("totalQty", extraHours.getTotalQty());
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  public void getExtraHoursImportConfig(ActionRequest request, ActionResponse response) {

    ImportConfiguration extraHoursImportConfig =
        Beans.get(ImportConfigurationRepository.class)
            .all()
            .filter(
                "self.bindMetaFile.fileName = ?1",
                ImportExtraHoursConfiguration.IMPORT_EXTRA_HOURS_CONFIG)
            .fetchOne();

    logger.debug("ImportConfig for lead: {}", extraHoursImportConfig);

    if (extraHoursImportConfig == null) {
      response.setFlash(I18n.get(IExceptionMessage.EXTRA_HOURS_1));
    } else {
      response.setView(
          ActionView.define(I18n.get(IExceptionMessage.EXTRA_HOURS_2))
              .model("com.axelor.apps.base.db.ImportConfiguration")
              .add("form", "import-configuration-form")
              .param("popup", "reload")
              .param("forceEdit", "true")
              .param("popup-save", "false")
              .param("show-toolbar", "false")
              .context("_showRecord", extraHoursImportConfig.getId().toString())
              .map());
    }
  }

  public void fetchExtraHours(ActionRequest request, ActionResponse response) {
    try {
      String accessToken = AppBaseController.getAccessToken();
      if (accessToken == null) {
        logger.error("Access token is null, unable to proceed.");
        return;
      }

      String jsonResponse = fetchHS(accessToken);

      if (jsonResponse != null) {
        try {
          JSONArray jsonArray = new JSONArray(jsonResponse);

          for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            Beans.get(ExtraHoursService.class).saveExtraHours(jsonObject);
          }

          // Reload the response after successfully saving extra hours
          response.setReload(true);

        } catch (JSONException e) {
          // Log the specific JSON parsing error
          System.err.println("Failed to parse JSON: " + jsonResponse);
          e.printStackTrace();
        }
      } else {
        System.err.println("No response received from fetchHS.");
      }
    } catch (Exception e) {
      // General catch for unexpected exceptions
      System.err.println("An error occurred while fetching extra hours: " + e.getMessage());
      e.printStackTrace();
    }
  }

  public String fetchHS(String accessToken) {
    String endpoint = BASE_URL + "tickets/hs/";
    HttpURLConnection conn = null;

    try {
      URL url = new URL(endpoint);
      conn = (HttpURLConnection) url.openConnection();

      conn.setRequestMethod("GET");
      conn.setRequestProperty("Content-Type", "application/json");
      conn.setRequestProperty("Accept", "application/json");
      // Include the access token in the Authorization header
      conn.setRequestProperty("Authorization", "Bearer " + accessToken);

      int responseCode = conn.getResponseCode();
      if (responseCode == HttpURLConnection.HTTP_OK) {
        try (BufferedReader br =
            new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
          StringBuilder response = new StringBuilder();
          String responseLine;
          while ((responseLine = br.readLine()) != null) {
            response.append(responseLine.trim());
          }
          // Process the response as needed
          return response.toString();
        }
      } else {
        // Read the error stream for more details
        StringBuilder errorResponse = new StringBuilder();
        try (BufferedReader br =
            new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"))) {
          String errorLine;
          while ((errorLine = br.readLine()) != null) {
            errorResponse.append(errorLine.trim());
          }
        }
        throw new RuntimeException(
            "Failed: HTTP error code: " + responseCode + ", Error: " + errorResponse.toString());
      }
    } catch (Exception e) {
      e.printStackTrace();
      return null; // Return null in case of failure
    } finally {
      if (conn != null) {
        conn.disconnect(); // Ensure connection is closed
      }
    }
  }
}
