package com.axelor.apps.account.service;

import com.axelor.apps.account.db.CashInventory;
import com.axelor.apps.account.db.CashInventoryLine;
import com.axelor.apps.account.db.CashRegister;
import com.axelor.apps.account.db.repo.CashDenominationRepository;
import com.axelor.apps.account.db.repo.CashInventoryRepository;
import com.axelor.apps.account.db.repo.CashRegisterRepository;
import com.axelor.apps.account.exception.IExceptionMessage;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CashInventoryService {

  private SequenceService sequenceService;

  @Inject
  public CashInventoryService(SequenceService sequenceService) {
    this.sequenceService = sequenceService;
  }

  /**
   * Initialize the theorical solde in words for a new cash inventory.
   *
   * @return the theorical solde in words as a BigDecimal
   */
  public BigDecimal initThetoricalSolde() {
    CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class);
    CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne();
    BigDecimal theoricalSolde = BigDecimal.ZERO;
    if (cashRegister == null) {
      theoricalSolde = BigDecimal.ZERO;
    } else {
      theoricalSolde = cashRegister.getTheoricalSolde();
    }
    return theoricalSolde;
  }

  /**
   * Computes the totals for the cash inventory, including total bank notes, total
   * coins, physical
   * solde, and gap.
   *
   * @param cashInventory The cash inventory to compute totals for.
   * @return A map containing the computed totals.
   */
  public Map<String, BigDecimal> computeToTals(CashInventory cashInventory) {

    HashMap<String, BigDecimal> map = new HashMap<>();

    List<CashInventoryLine> cashInventoryLines = cashInventory.getCashInventoryLines();

    BigDecimal theoricalSolde = cashInventory.getTheoricalSolde();

    BigDecimal totalBankNotes = BigDecimal.ZERO;
    BigDecimal totalCoins = BigDecimal.ZERO;
    BigDecimal physicalSolde = BigDecimal.ZERO;
    BigDecimal gap = BigDecimal.ZERO;

    for (CashInventoryLine cashInventoryLine : cashInventoryLines) {
      switch (cashInventoryLine.getCashDenomination().getTypeSelect()) {
        case CashDenominationRepository.BANK_NOTE_TYPE:
          totalBankNotes = totalBankNotes.add(cashInventoryLine.getTotalCashCount());
          break;
        case CashDenominationRepository.COINT_TYPE:
          totalCoins = totalCoins.add(cashInventoryLine.getTotalCashCount());
          break;

        default:
          totalBankNotes = BigDecimal.ZERO;
          totalCoins = BigDecimal.ZERO;
          break;
      }
    }

    physicalSolde = physicalSolde.add(totalBankNotes).add(totalCoins);
    gap = physicalSolde.subtract(theoricalSolde);

    map.put("totalCoinsAmount", totalCoins);
    map.put("totalBankNotesAmount", totalBankNotes);
    map.put("totalCash", totalBankNotes);
    map.put("physicalSolde", physicalSolde);
    map.put("gap", gap);

    return map;
  }

  public String getCashInventorySequence(CashInventory cashInventory) throws AxelorException {
    Integer operationTypeSelect = cashInventory.getOperationTypeSelect();
    Company company = cashInventory.getCompany();
    String sequence = null;

    if (operationTypeSelect != null) {
      switch (operationTypeSelect) {
        case 1: // Cash inventory daily
          sequence = "CASH_INVENTORY_DAILY";
          break;
        case 2: // Cash inventory monthly
          sequence = "CASH_INVENTORY_MONTHLY";
          break;
        case 3: // Cash inventory annual
          sequence = "CASH_INVENTORY_ANNUAL";
          break;
        default:
          sequence = "CASH_INVENTORY";
          // Default case for other operation types
          break;
      }
    } else {
      sequence = "CASH_INVENTORY";
    }

    String ref = sequenceService.getSequenceNumber(sequence, company);
    if (ref == null) {
      throw new AxelorException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(IExceptionMessage.CASH_INVENTORY_MISSING_SEQUENCE),
          company.getName());
    } else {
      return ref;
    }
  }


  @Transactional(rollbackOn = {Exception.class})
  public void approveCashInventory(CashInventory cashInventory) {
    CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class);
    CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne();
    // cashRegister.setTheoricalSolde(cashInventory.getPhysicalSolde());
    cashRegister.setPhysicalSolde(cashInventory.getPhysicalSolde());
    cashInventory.setValdiatedByUser(AuthUtils.getUser());
    cashInventory.setValidattionDate(LocalDateTime.now());
    cashInventory.setStatusSelect(3);
    Beans.get(CashInventoryRepository.class).save(cashInventory);
    casRegisterRepository.save(cashRegister);
  }
}
