import java.io.IOException;
import java.io.DataInputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.time.Month;
import java.time.YearMonth;
import java.util.Locale;

class Darlehenberechner {

   private static final class Konfiguration {

      private BigDecimal darlehenswert;
      private BigDecimal zinssatzReal;
      private BigDecimal monatlicheRate;
      private Integer laufzeitJahre;
      private Integer aktTilgungsfreieZeit;
      private YearMonth anfangsmonat;
      private BigDecimal sondertilgungReal;


      public BigDecimal getDarlehenswert() {
         return darlehenswert;
      }


      public Konfiguration setDarlehenswert(BigDecimal darlehenswert) {
         this.darlehenswert = darlehenswert;
         return this;
      }


      public BigDecimal getZinssatz() {
         return zinssatzReal;
      }


      public Konfiguration setZinssatzProzent(BigDecimal zinssatzProzent) {
         this.zinssatzReal = zinssatzProzent.divide(EINHUNDERT, MathContext.DECIMAL128);
         return this;
      }


      public BigDecimal getMonatlicheRate() {
         return monatlicheRate;
      }


      public Konfiguration setMonatlicheRate(BigDecimal monatlicheRate) {
         this.monatlicheRate = monatlicheRate;
         return this;
      }


      public Integer getLaufzeitJahre() {
         return laufzeitJahre;
      }


      public Konfiguration setLaufzeitJahre(Integer laufzeitJahre) {
         this.laufzeitJahre = laufzeitJahre;
         return this;
      }


      public Integer getTilgungsfreieZeit() {
         return tilgungsfreieZeit;
      }


      public Konfiguration setTilgungsfreieZeit(Integer tilgungsfreieZeit) {
         this.tilgungsfreieZeit = tilgungsfreieZeit;
         return this;
      }


      public YearMonth getAnfangsmonat() {
         return anfangsmonat;
      }


      public Konfiguration setAnfangsmonat(YearMonth anfangsmonat) {
         this.anfangsmonat = anfangsmonat;
         return this;
      }


      public BigDecimal getSondertilgung() {
         return sondertilgungReal;
      }


      public Konfiguration setSondertilgungProzent(BigDecimal sondertilgungProzent) {
         this.sondertilgungReal = sondertilgungProzent.divide(EINHUNDERT, MathContext.DECIMAL128);
         return this;
      }
   }

   private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Locale.GERMAN));
   private static final BigDecimal ZWOELF = BigDecimal.valueOf(12);
   private static final BigDecimal EINHUNDERT = BigDecimal.valueOf(100);

   private final Integer laufzeitJahre;
   private final BigDecimal zinssatz;
   private BigDecimal sondertilgung;
   private int summeMonate = 0;
   private BigDecimal aktRestschuld;
   private BigDecimal aktMonatlicheRate;
   private BigDecimal aktZinsbetrag;
   private BigDecimal aktTilgungsbetrag;
   private Integer aktTilgungsfreieZeit;
   private YearMonth aktMonat;
   private BigDecimal jahressummeZinsenKalenderjahr = BigDecimal.ZERO;
   private BigDecimal jahressummeZinsenKreditjahr = BigDecimal.ZERO;
   private BigDecimal summeZinsen = BigDecimal.ZERO;
   private BigDecimal jahressummeTilgungKalenderjahr = BigDecimal.ZERO;
   private BigDecimal jahressummeTilgungKreditjahr = BigDecimal.ZERO;
   private BigDecimal summeTilgung = BigDecimal.ZERO;
   private BigDecimal jahressummeRatenKalenderjahr = BigDecimal.ZERO;
   private BigDecimal jahressummeRatenKreditjahr = BigDecimal.ZERO;
   private BigDecimal summeRaten = BigDecimal.ZERO;


   public static void main(String[] args) throws ParseException, IOException {
      /*new Darlehenberechner(new Konfiguration()
         .setDarlehenswert(BigDecimal.valueOf(168_000))
         .setZinssatzProzent(BigDecimal.valueOf(3.73))
         .setMonatlicheRate(BigDecimal.valueOf(1_500))
         .setTilgungsfreieZeit(0)
         .setAnfangsmonat(YearMonth.of(2024, Month.SEPTEMBER))
         .setSondertilgungProzent(BigDecimal.valueOf(2.5))
      ).berechneWerte();*/

      /*new Darlehenberechner(new Konfiguration()
         .setDarlehenswert(BigDecimal.valueOf(168_000))
         .setZinssatzProzent(BigDecimal.valueOf(3.73))
         .setMonatlicheRate(BigDecimal.valueOf(1_500))
         .setTilgungsfreieZeit(0)
         .setLaufzeitJahre(11)
         .setAnfangsmonat(YearMonth.of(2024, Month.SEPTEMBER))
      ).berechneWerte();*/

      DECIMAL_FORMAT.setParseBigDecimal(true);

      var konfig = new Konfiguration();
      var dis = new DataInputStream(System.in);
      System.out.print("Darlehenswert: ");
      konfig.setDarlehenswert((BigDecimal) DECIMAL_FORMAT.parse(dis.readLine()));
      System.out.print("Zinssatz: ");
      konfig.setZinssatzProzent((BigDecimal) DECIMAL_FORMAT.parse(dis.readLine()));
      System.out.print("Monatliche Rate: ");
      konfig.setMonatlicheRate((BigDecimal) DECIMAL_FORMAT.parse(dis.readLine()));
      System.out.print("Laufzeit in Jahren(optional): ");
      var in = dis.readLine();
      if (in != null && !in.isBlank()) {
         konfig.setLaufzeitJahre(Integer.parseInt(dis.readLine()));
      }
      System.out.print("Anzahl tilgungsfreier Monate(optional): ");
      in = dis.readLine();
      if (in != null && !in.isBlank()) {
         konfig.setTilgungsfreieZeit(Integer.parseInt(dis.readLine()));
      }
      System.out.print("Monat erste Rate(z.B. 2007-12): ");
      konfig.setAnfangsmonat(YearMonth.parse(dis.readLine()));
      System.out.print("Sondertilgungssatz(optional): ");
      in = dis.readLine();
      if (in != null && !in.isBlank()) {
         konfig.setSondertilgungProzent((BigDecimal) DECIMAL_FORMAT.parse(in));
      }
      new Darlehenberechner(konfig).berechneWerte();

      /*var konfig = new Konfiguration();
      int count = 0;
      DECIMAL_FORMAT.setParseBigDecimal(true);
      while (count < args.length) {
         String arg = args[count];
         if (arg.equals("-hilfe")) {
            System.out.println("-darlehenswert 1000,00 -zinssatz 3,73 -monatlicheRate 30,00 -anfangsmonat 2024-09"
               + "[-laufzeitJahre 11] [-aktTilgungsfreieZeit 5]");
         }
         if (arg.equals("-darlehenswert")) {
            count++;
            konfig.setDarlehenswert((BigDecimal) DECIMAL_FORMAT.parse(args[count]));
         }
         if (arg.equals("-zinssatz")) {
            count++;
            konfig.setZinssatzProzent((BigDecimal) DECIMAL_FORMAT.parse(args[count]));
         }
         if (arg.equals("-sondertilgung")) {
            count++;
            konfig.setSondertilgungProzent((BigDecimal) DECIMAL_FORMAT.parse(args[count]));
         }
         if (arg.equals("-monatlicheRate")) {
            count++;
            konfig.setMonatlicheRate((BigDecimal) DECIMAL_FORMAT.parse(args[count]));
         }
         if (arg.equals("-aktTilgungsfreieZeit")) {
            count++;
            konfig.setTilgungsfreieZeit(Integer.parseInt(args[count]));
         }
         if (arg.equals("-laufzeitJahre")) {
            count++;
            konfig.setLaufzeitJahre(Integer.parseInt(args[count]));
         }
         if (arg.equals("-anfangsmonat")) {
            count++;
            konfig.setAnfangsmonat(YearMonth.parse(args[count]));
         }
         count++;
      }
      new Darlehenberechner(konfig).berechneWerte();*/
   }


   private Darlehenberechner(Konfiguration konfig) {
      laufzeitJahre = konfig.getLaufzeitJahre();
      zinssatz = konfig.getZinssatz();
      sondertilgung = konfig.getSondertilgung() != null ? konfig.getSondertilgung().multiply(konfig.getDarlehenswert()) : BigDecimal.ZERO;
      aktRestschuld = konfig.getDarlehenswert();
      aktMonatlicheRate = konfig.getMonatlicheRate();
      aktTilgungsfreieZeit = konfig.getTilgungsfreieZeit();
      aktMonat = konfig.getAnfangsmonat();
   }


   private void berechneWerte() {
      druckeUeberschrift();
      while (laufzeitNichtVorbei()) {
         erhoeheSummeMonate();
         zahleSondertilgung();
         berechneBeitragAufteilung();
         berechneRestschuld();
         druckeAktuelleMonatswerte();
         berechneSummen();
         druckeJahressumeBedingt();
         aktualisiereZeitwerte();
      }
      druckeFinaleSummen();
      druckeLaufzeitUndRestschuld();
   }


   private void druckeUeberschrift() {
      System.out.println("Monat:   Rate     = Zinsen + Tilgung| Restschuld");
   }


   private boolean laufzeitNichtVorbei() {
      return (laufzeitJahre == null || summeMonate < (laufzeitJahre * 12)) && aktRestschuld.signum() > 0;
   }


   private boolean laufzeitVorbei() {
      return !laufzeitNichtVorbei();
   }


   private void erhoeheSummeMonate() {
      summeMonate++;
   }


   private void zahleSondertilgung() {
      if (sondertilgung == null || sondertilgung.signum() == 0) {
         return;
      }
      boolean sondertilgungFaellingErstesJahr = summeMonate < 12 && aktMonat.getMonth() == Month.DECEMBER;
      if (sondertilgungFaellingErstesJahr || summeMonate > 1 && aktMonat.getMonth() == Month.JANUARY) {
         aktRestschuld = aktRestschuld.subtract(sondertilgung);
         System.out.println(aktMonat + ": " + DECIMAL_FORMAT.format(sondertilgung) + " = 0,00 + " + DECIMAL_FORMAT.format(sondertilgung) + " | " + DECIMAL_FORMAT.format(aktRestschuld));
      }
   }


   private void berechneBeitragAufteilung() {
      aktZinsbetrag = aktRestschuld.multiply(zinssatz)
         .divide(ZWOELF, MathContext.DECIMAL128);
      if (aktMonatlicheRate.compareTo(aktRestschuld) > 0) {
         aktMonatlicheRate = aktRestschuld.add(aktZinsbetrag); // die letzte Rate ist gleich der Restschuld + Zinsen
      }
      aktTilgungsbetrag = aktTilgungsfreieZeit != null && aktTilgungsfreieZeit > 0 ? BigDecimal.ZERO : aktMonatlicheRate.subtract(aktZinsbetrag);
   }


   private void berechneRestschuld() {
      aktRestschuld = aktRestschuld.subtract(aktTilgungsbetrag);
   }


   private void druckeAktuelleMonatswerte() {
      System.out.println(aktMonat + ": " + DECIMAL_FORMAT.format(aktMonatlicheRate) + " = " + DECIMAL_FORMAT.format(aktZinsbetrag)
            + " + " + DECIMAL_FORMAT.format(aktTilgungsbetrag) + " | " + DECIMAL_FORMAT.format(aktRestschuld));
   }


   private void berechneSummen() {
      jahressummeZinsenKalenderjahr = jahressummeZinsenKalenderjahr.add(aktZinsbetrag);
      jahressummeZinsenKreditjahr = jahressummeZinsenKreditjahr.add(aktZinsbetrag);
      summeZinsen = summeZinsen.add(aktZinsbetrag);
      jahressummeTilgungKalenderjahr = jahressummeTilgungKalenderjahr.add(aktTilgungsbetrag);
      jahressummeTilgungKreditjahr = jahressummeTilgungKreditjahr.add(aktTilgungsbetrag);
      summeTilgung = summeTilgung.add(aktTilgungsbetrag);
      jahressummeRatenKalenderjahr = jahressummeRatenKalenderjahr.add(aktMonatlicheRate);
      jahressummeRatenKreditjahr = jahressummeRatenKreditjahr.add(aktMonatlicheRate);
      summeRaten = summeRaten.add(aktMonatlicheRate);
   }


   private void druckeJahressumeBedingt() {
      boolean kreditjahrVergangen = summeMonate > 1 && summeMonate % 12 == 0 || laufzeitVorbei();
      if (kreditjahrVergangen || aktMonat.getMonth() == Month.DECEMBER) {
         BigDecimal jahressummeZinsen;
         BigDecimal jahressummeTilgung;
         BigDecimal jahressummeRaten;
         String desc;
         if (kreditjahrVergangen) {
            jahressummeZinsen = jahressummeZinsenKreditjahr;
            jahressummeTilgung = jahressummeTilgungKreditjahr;
            jahressummeRaten = jahressummeRatenKreditjahr;
            jahressummeZinsenKreditjahr = BigDecimal.ZERO;
            jahressummeTilgungKreditjahr = BigDecimal.ZERO;
            jahressummeRatenKreditjahr = BigDecimal.ZERO;
            desc = "Kreditjahr " + summeMonate / 12;
         } else {
            jahressummeZinsen = jahressummeZinsenKalenderjahr;
            jahressummeTilgung = jahressummeTilgungKalenderjahr;
            jahressummeRaten = jahressummeRatenKalenderjahr;
            jahressummeZinsenKalenderjahr = BigDecimal.ZERO;
            jahressummeTilgungKalenderjahr = BigDecimal.ZERO;
            jahressummeRatenKalenderjahr = BigDecimal.ZERO;
            desc = "Kalenderjahr " + aktMonat.getYear();
         }
         System.out.println("Summe " + desc + ":\n" + DECIMAL_FORMAT.format(jahressummeRaten) + " = " + DECIMAL_FORMAT.format(jahressummeZinsen)
            + " + " + DECIMAL_FORMAT.format(jahressummeTilgung));
      }
   }


   private void aktualisiereZeitwerte() {
      aktMonat = aktMonat.plusMonths(1);
      if (aktTilgungsfreieZeit != null) {
         aktTilgungsfreieZeit--;
      }
   }


   private void druckeFinaleSummen() {
      System.out.println("Summe:\n" + DECIMAL_FORMAT.format(summeRaten) + " = " + DECIMAL_FORMAT.format(summeZinsen)
         + " + " + DECIMAL_FORMAT.format(summeTilgung));
   }


   private void druckeLaufzeitUndRestschuld() {
      int laufzeitJahreFinal = laufzeitJahre == null ? summeMonate / 12 : laufzeitJahre;
      int teillaufzeitMonateFinal = summeMonate - (laufzeitJahreFinal * 12);
      System.out.println("Laufzeit: " + laufzeitJahreFinal + " Jahre " + teillaufzeitMonateFinal + " Monate");
      System.out.println("Restschuld: " + DECIMAL_FORMAT.format(aktRestschuld));
   }
}