#!/usr/bin/env python3

# Excel_Bruecke.py
"""
Kapitel Einfuehrung: Vom Excel-Solver zu Python - dieselbe Rechnung, dieselbe Datei.

Das Programm bildet den vollstaendigen Arbeitsweg ab, den ein Excel-Modell im
Betrieb sonst von Hand geht:

    Tabellenblatt lesen -> Modell bauen -> loesen -> Ergebnis zurueckschreiben

Damit es ohne Vorbereitung laeuft, legt es die Eingabedatei beim ersten Start
selbst an. Ersetzen Sie sie durch Ihre eigene Datei - solange die Spalten
gleich heissen, aendert sich am Code nichts.

Die vier Arbeitsschritte kommen aus or_kern.py (Kapitel Praxisfallen). Was
hier steht, ist nur noch das, was diesen Fall ausmacht - der Rest ist
gemeinsamer Unterbau. Wer das Modul nicht kennt: Es ist im Buch vollstaendig
abgedruckt und liegt im selben Verzeichnis.

Erzeugte Dateien (im Arbeitsverzeichnis):
    produktionsmix.xlsx           Eingabe:  Blaetter 'Produkte' und 'Kapazitaeten'
    produktionsmix_ergebnis.xlsx  Ausgabe:  Blaetter 'Plan' und 'Kennzahlen'

Benoetigt: pandas, openpyxl, ortools, pydantic (ueber or_kern)
"""

from __future__ import annotations

import os

import pandas as pd

from or_kern import (lade_produktionsproblem, loese_mit_glop, pruefe_loesung,
                     schreibe_ergebnis)

# Die Mappen liegen NEBEN diesem Programm, nicht im aktuellen
# Arbeitsverzeichnis. Sonst haengt es davon ab, aus welchem Ordner man das
# Skript startet, wo die Dateien landen - und wer es aus der
# Repository-Wurzel aufruft, verstreut sie dort.
HIER = os.path.dirname(os.path.abspath(__file__))
EINGABE = os.path.join(HIER, "produktionsmix.xlsx")
AUSGABE = os.path.join(HIER, "produktionsmix_ergebnis.xlsx")


def lege_beispieldatei_an(pfad: str) -> None:
    """Schreibt die Eingabemappe, wie sie auch aus dem Controlling kaeme."""
    produkte = pd.DataFrame({
        "Produkt": ["Tisch", "Stuhl", "Regal"],
        "Deckungsbeitrag": [240.0, 60.0, 130.0],
        "Montagestunden": [3.0, 1.0, 2.0],
        "Plattenmaterial": [6.0, 1.0, 4.0],
    })
    kapazitaeten = pd.DataFrame({
        "Ressource": ["Montagestunden", "Plattenmaterial"],
        "Verfuegbar": [150.0, 240.0],
    })
    with pd.ExcelWriter(pfad, engine="openpyxl") as mappe:
        produkte.to_excel(mappe, sheet_name="Produkte", index=False)
        kapazitaeten.to_excel(mappe, sheet_name="Kapazitaeten", index=False)


if __name__ == "__main__":
    if not os.path.exists(EINGABE):
        lege_beispieldatei_an(EINGABE)
        print(f"Beispiel-Eingabedatei angelegt: {EINGABE}")

    # 1. Lesen und pruefen. Faellt hier etwas auf, ist der Fehler noch
    #    zuzuordnen - spaeter waere er nur noch eine merkwuerdige Zahl.
    problem = lade_produktionsproblem(EINGABE)

    # 2. Loesen. Einzige solverabhaengige Zeile des ganzen Programms -
    #    'loese_mit_scipy' waere ein Einzeiler-Wechsel.
    loesung = loese_mit_glop(problem)

    # 3. Abnahmepruefung gegen die Anforderungen, ohne den Solver zu fragen.
    beanstandungen = pruefe_loesung(problem, loesung)
    if beanstandungen:
        raise SystemExit("Abnahmepruefung fehlgeschlagen:\n  - "
                         + "\n  - ".join(beanstandungen))

    # 4. Zurueckschreiben - im Format, das die Fachabteilung ohnehin benutzt.
    schreibe_ergebnis(AUSGABE, problem, loesung)

    print("=" * 66)
    print("  PRODUKTIONSPLAN AUS DER EXCEL-MAPPE")
    print("=" * 66)
    for produkt in problem.produkte:
        menge = loesung.werte[produkt.name]
        print(f"{produkt.name:<18} {menge:8.1f} Stueck "
              f"{menge * produkt.deckungsbeitrag:12,.2f} EUR")
    print("-" * 66)
    print(loesung.als_bericht())
    print("Abnahmepruefung: bestanden\n")

    # Die Schattenpreise beantworten die Frage, die der Plan nicht stellt:
    # Was waere eine zusaetzliche Einheit dieser Ressource wert?
    for ressource, preis in loesung.schattenpreise.items():
        print(f"  eine Einheit {ressource:<18} mehr waere wert: {preis:7.2f} EUR")
    print("=" * 66)
    print(f"Ergebnis geschrieben nach: {AUSGABE}")
