
from datetime import date
from django.db import connection


from app_verbrauch.models import VISysVerbrauchRahmen

from django.db import models as django_models
from app_nutzer.models import ReuMietV
from app_real_estate.models import RealEstateUnit, RealEstate
from app_real_estate.models import RealEstateUnit

from .models import (
    VISysVerbrauchRahmen,
    VISysVerbrauchsDS,
    VISysRefVerbrauchRahmen,
    VISysRefVerbrauchsDS,
    VISysVerbrauchArt,
)
# -----------------------------------------------------------------------
# Constants (migrated from static class properties)
# ---------------------------------------------------------------------------
DIAGRAMM_TYPES = [99, 100, 101, 102, 110, 111]
DIAGRAMM_TYPES_SPEZ = [99, 100, 101, 102, 110]

MONATE = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]

MONATE_LANG = [
    "Januar", "Februar", "März", "April", "Mai", "Juni",
    "Juli", "August", "September", "Oktober", "November", "Dezember"
    ]

COLORS_REFERENZ = ["#d9d9d9", "#999999", "#a6a6a6"]

DIAGRAMM_STYLE_AND_TEXT = {
    99: {
        'colorstack': ["#FFC000", "#e1a200", "#c38400", "#a56600"],
        'Title': 'Energieverbrauch für Raumwärme (erfasst durch HKV)',
        'TitleSpez': 'Verbrauch/m² der Heizkostenverteiler (Raumwärme)',
        'MassEinheit': 'gHKV',
        'MassEinheitSpez': 'gHKV/m²',
        'Text': (
            "Dies ist der gewichtete, erfasste Verbrauch der Heizkostenverteiler im Gebäude.<br><br>"
            "Die Gewichtung berücksichtigt die unterschiedliche thermische Leistung der Heizkörper. "
            "Weitere Information zur Gewichtung können Sie der Heizkostenabrechnung entnehmen. "
            "Angaben in gHKV können nicht in kWh umgerechnet werden."
        ),
        'TextSpez': (
            "Beim Verbrauchsvergleich wird der auf die Wohnfläche von 1 m² normierte Verbrauch "
            "der Heizkostenverteiler in Ihrer Einheit mit dem Verbrauch des Durchschnittsnutzers "
            "derselben Kategorie verglichen. Der Verbrauch des Durchschnittsnutzers ist dabei der Referenzwert. "
            "Angaben in gHKV können nicht in kWh umgerechnet werden."
        ),
    },
    100: {
        'colorstack': ["#FFC000", "#e1a200", "#c38400", "#a56600"],
        'Title': 'Energiebedarf für Raumwärme',
        'TitleSpez': 'Energiebedarf für Raumwärme (normiert)',
        'MassEinheit': 'kWh',
        'MassEinheitSpez': 'kWh/m²',
        'Text': "Dies ist der Energiebedarf zur Bereitstellung von Raumwärme (Referenzwert: Gesamtbedarf im Gebäude)",
        'TextSpez': (
            "Beim Verbrauchsvergleich wird der auf die Wohnfläche von 1 m² normierte Energiebedarf "
            "zur Bereitstellung von Raumwärme in Ihrer Einheit mit dem Energiebedarf (=Referenzwert) "
            "des Durchschnittsnutzers derselben Kategorie verglichen. "
            "Der Verbrauch des Durchschnittsnutzers ist dabei der Referenzwert."
        ),
    },
    101: {
        'colorstack': ["#FF6600", "#e14800", "#c32a00", "#a50c00"],
        'Title': 'Energiebedarf für Trinkwarmwasser',
        'TitleSpez': 'Energiebedarf für Trinkwarmwasser (normiert)',
        'MassEinheit': 'kWh',
        'MassEinheitSpez': 'kWh/m²',
        'Text': "Dies ist der Energiebedarf zur Bereitstellung von Trinkwarmwasser (Referenzwert: Gesamtbedarf im Gebäude)",
        'TextSpez': (
            "Beim Verbrauchsvergleich wird der auf die Wohnfläche von 1 m² normierte Energiebedarf "
            "zur Bereitstellung von Trinkwarmwasser in Ihrer Einheit mit dem Energiebedarf "
            "(=Referenzwert) des Durchschnittsnutzers derselben Kategorie verglichen."
        ),
    },
    102: {
        'colorstack': ["#FF9300", "#e17400", "#c35700", "#a53900"],
        'Title': 'Energiebedarf für Wärme',
        'TitleSpez': 'Energiebedarf für Wärme (normiert)',
        'MassEinheit': 'kWh',
        'MassEinheitSpez': 'kWh/m²',
        'Text': "Dies ist der Energieverbrauch, den Ihre Einheit von der zentralen Wärmeversorgung bezogen hat. (Referenzwert: Gesamtbedarf im Gebäude)",
        'TextSpez': (
            "Beim Verbrauchsvergleich wird die auf die Wohnfläche von 1 m² normierte gelieferte Wärme "
            "in Ihrer Einheit mit der gelieferten Wärme (=Referenzwert) "
            "des Durchschnittsnutzers derselben Kategorie verglichen. "
            "Der Verbrauch des Durchschnittsnutzers ist dabei der Referenzwert. "
            "Raumwärme und Wärme für Trinkwarmwasser werden dabei nicht getrennt erfasst."
        ),
    },
    110: {
        'colorstack': ["#205AFC", "#023cde", "#001ec0", "#0000a2"],
        'Title': 'Verbrauch von Trinkwasser (gesamt)',
        'TitleSpez': 'Verbrauch von Trinkwasser (normiert)',
        'MassEinheit': 'm³',
        'MassEinheitSpez': 'm³/m²',
        'Text': (
            "Dies gibt den Verbrauch von Trinkwasser im Gebäude an. "
            "Darin ist das Trinkwasser für Kalt- & Warmwasser enthalten. "
            "Der Referenzwert gibt den Gesamtverbrauch im Gebäude an."
        ),
        'TextSpez': (
            "Beim Verbrauchsvergleich wird der auf die Wohnfläche von 1 m² normierte Trinkwasserverbrauch "
            "in Ihrer Einheit mit dem Trinkwasserverbrauch (=Referenzwert) "
            "des Durchschnittsnutzers derselben Kategorie verglichen. "
            "Der Verbrauch des Durchschnittsnutzers ist dabei der Referenzwert."
        ),
    },
    111: {
        'colorstack': ["#E21914", "#c40000", "#a60000", "#880000"],
        'Title': 'Verbrauch für Trinkwarmwasser',
        'TitleSpez': 'Verbrauch für Trinkwarmwasser',
        'MassEinheit': 'm³',
        'MassEinheitSpez': 'm³/m²',
        'Text': "Dies gibt den Verbrauch von Trinkwarmwasser im Gebäude an. Der Referenzwert gibt den Gesamtverbrauch im Gebäude an.",
        'TextSpez': "",
    },
    999: {
        'colorstack': ["#ffffff", "#ffffff", "#ffffff", "#ffffff"],
        'Title': '',
        'TitleSpez': '',
        'MassEinheit': 'o.E.',
        'MassEinheitSpez': 'o.E.',
        'Text': "Keine Weiteren Informationen hinterlegt",
        'TextSpez': "",
    },
}


def _de_zahl(wert):
    """Formatiert eine Zahl im deutschen Format (1.234,56).

    Bewusst nur auf den Zahlenwert anwenden - fruehere Varianten haben die
    Ersetzung auf den ganzen Satz angewendet und damit Satzpunkte zu Kommas
    gemacht.
    """
    return f"{wert:,.2f}".replace(',', 'COMMA').replace('.', ',').replace('COMMA', '.')


# Verbrauchsarten ohne Energiebezug (reine Wassermengen) - hier passt der
# Hinweis auf den normierten Energieverbrauch nicht.
VERBRAUCHSARTEN_OHNE_ENERGIE_HINWEIS = (110, 111)


def get_verbrauchs_jahre():
    """Returns last 3 years including current year. Migrated from static method."""
    current = date.today().year
    return [current, current - 1, current - 2]


def get_all_dash_cards_data(reuid, nutzer_id):
    """Migrated from VerbrauchsChartDaten::getAllDashCardsData()."""
    result = []
    for diagramm_type in DIAGRAMM_TYPES:
        if _check_verbrauchsrahmen_exists(diagramm_type, reuid, nutzer_id):
            card = get_dash_card_daten(diagramm_type, reuid, nutzer_id)
            if card:
                result.append(card)
    return result



def get_verbrauchs_chart_daten(verbrauchsart, mietv_id):
    """
    Migrated from VerbrauchsChartDaten::getVerbauchsChartDaten().
    Returns chart data dict for a single consumption type.
    """
    style = DIAGRAMM_STYLE_AND_TEXT.get(verbrauchsart, DIAGRAMM_STYLE_AND_TEXT[999])
    title = style['Title']
    text = style['Text']
    mass_einheit = style['MassEinheit']
    colorstack = style['colorstack']

    reu_data = _fetch_reu_data(mietv_id)
    reid = reu_data['REID'] if reu_data else None

    jahre = get_verbrauchs_jahre()
    chart_bars = []

    for i, jahr in enumerate(jahre):
        # User consumption values
        rows = _fetch_verbrauchswerte(verbrauchsart, jahr, mietv_id, 'VerBrWert')
        if rows:
            monthly = _rows_to_monthly_array(rows)
            chart_bars.append({
                'color': colorstack[i] if i < len(colorstack) else '#ccc',
                'year': str(jahr),
                'data': monthly,
            })

        # Reference values
        if reid:
            ref_rows = _fetch_referenzwerte(verbrauchsart, jahr, reid, 'RefVerBrWert')
            if ref_rows:
                ref_monthly = _rows_to_monthly_array(ref_rows)

                # Jahressumme berechnen wenn 12 Monatswerte vorliegen
                if len(rows) == 12:
                    jahres_sum_einheit = sum(float(r[0]) for r in rows if r[0] is not None)
                    jahres_sum_gebaeude = sum(float(r[0]) for r in ref_rows if r[0] is not None)
                    if jahres_sum_gebaeude > 0:
                        anteil = (jahres_sum_einheit / jahres_sum_gebaeude) * 100
                        text += (
                            f"<br><br>In Ihrer Einheit wurden im <b>Jahr {jahr}: "
                            f"{_de_zahl(jahres_sum_einheit)} {mass_einheit}</b> verbraucht. "
                            f"Das entspricht {_de_zahl(anteil)} % des Gesamtverbrauchs "
                            f"({_de_zahl(jahres_sum_gebaeude)} {mass_einheit}) des Gebäudes."
                        )

                chart_bars.append({
                    'color': COLORS_REFERENZ[i] if i < len(COLORS_REFERENZ) else '#ccc',
                    'year': f'{jahr} Referenz ',
                    'data': ref_monthly,
                })

    return {
        'Title': title,
        'color': colorstack[0] if colorstack else '#ccc',
        'Text': text,
        'ChartType': verbrauchsart,
        'MassEinheit': mass_einheit,
        'charts': chart_bars,
    }


def get_verbrauchs_chart_daten_neu(verbrauchsart, reuid, nutzer_id):
    style        = DIAGRAMM_STYLE_AND_TEXT.get(verbrauchsart, DIAGRAMM_STYLE_AND_TEXT[999])
    title        = style['Title']
    text         = style['Text']
    mass_einheit = style['MassEinheit']
    colorstack   = style['colorstack']

    reu_data = RealEstateUnit.objects.filter(REUID=reuid).values('REUID', 'REID').first()
    reid     = reu_data['REID'] if reu_data else None
    jahre    = get_verbrauchs_jahre()
    chart_bars = []

    for i, jahr in enumerate(jahre):
        rows = _fetch_verbrauchswerte(verbrauchsart, jahr, reuid, nutzer_id, 'VerBrWert')
        if rows:
            monthly = _rows_to_monthly_array(rows)
            chart_bars.append({
                'color': colorstack[i] if i < len(colorstack) else '#ccc',
                'year':  str(jahr),
                'data':  monthly,
            })

        if reid:
            ref_rows = _fetch_referenzwerte(verbrauchsart, jahr, reid, 'RefVerBrWert')
            if ref_rows:
                ref_monthly = _rows_to_monthly_array(ref_rows)
                if len(rows) == 12:
                    jahres_sum_einheit   = sum(float(r[0]) for r in rows     if r[0] is not None)
                    jahres_sum_gebaeude  = sum(float(r[0]) for r in ref_rows if r[0] is not None)
                    if jahres_sum_gebaeude > 0:
                        anteil = (jahres_sum_einheit / jahres_sum_gebaeude) * 100
                        text += (
                            f"<br><br>In Ihrer Einheit wurden im <b>Jahr {jahr}: "
                            f"{_de_zahl(jahres_sum_einheit)} {mass_einheit}</b> verbraucht. "
                            f"Das entspricht {_de_zahl(anteil)} % des Gesamtverbrauchs "
                            f"({_de_zahl(jahres_sum_gebaeude)} {mass_einheit}) des Gebäudes."
                        )
                chart_bars.append({
                    'color': COLORS_REFERENZ[i] if i < len(COLORS_REFERENZ) else '#ccc',
                    'year':  f'{jahr} Referenz ',
                    'data':  ref_monthly,
                })

    return {
        'Title':      title,
        'color':      colorstack[0] if colorstack else '#ccc',
        'Text':       text,
        'ChartType':  verbrauchsart,
        'MassEinheit': mass_einheit,
        'charts':     chart_bars,
    }

def get_verbrauchs_chart_daten_spez(verbrauchsart, reuid, nutzer_id):
    """
    Migrated from VerbrauchsChartDaten::getVerbauchsChartDatenSpez().
    Returns normalised (specific) chart data.
    """
    style = DIAGRAMM_STYLE_AND_TEXT.get(verbrauchsart, DIAGRAMM_STYLE_AND_TEXT[999])
    title = style['TitleSpez']
    text = style['TextSpez']
    mass_einheit = style['MassEinheitSpez']
    colorstack = style['colorstack']

    reu_data = RealEstateUnit.objects.filter(REUID=reuid).values('REUID', 'REID').first()
    reid     = reu_data['REID'] if reu_data else None
    jahre    = get_verbrauchs_jahre()
    chart_bars = []


    for i, jahr in enumerate(jahre):
        rows = _fetch_verbrauchswerte(verbrauchsart, jahr, reuid, nutzer_id, 'VerBrWertNormA')
   
        if rows:
            monthly = _rows_to_monthly_array(rows)
            chart_bars.append({
                'color': colorstack[i] if i < len(colorstack) else '#ccc',
                'year': str(jahr),
                'data': monthly,
            })

        if reid:
            ref_rows = _fetch_referenzwerte(verbrauchsart, jahr, reid, 'RefVerBrWertNormA')
            if ref_rows:
                ref_monthly = _rows_to_monthly_array(ref_rows)

                if len(rows) == 12:
                    spez_einheit = sum(float(r[0]) for r in rows if r[0] is not None)
                    spez_gebaeude = sum(float(r[0]) for r in ref_rows if r[0] is not None)
                    delta = spez_einheit - spez_gebaeude
                    text += (
                        f"<br><br>In Ihrer Einheit lag der <b>normierte Verbrauch</b> im "
                        f"<b>Jahr {jahr}</b> bei <b>{_de_zahl(spez_einheit)} {mass_einheit}</b>. "
                    )
                    if delta > 0:
                        text += (
                            f"Der Verbrauch in Ihrer Einheit war somit <b>{_de_zahl(abs(delta))} {mass_einheit} höher</b> "
                            f"als der Verbrauch unseres Durchschnittsnutzers ({_de_zahl(spez_gebaeude)} {mass_einheit}) "
                            f"im gleichen Zeitraum."
                        )
                    else:
                        text += (
                            f"Der Verbrauch der Einheit war somit <b>{_de_zahl(abs(delta))} {mass_einheit} niedriger</b> "
                            f"als der Verbrauch unseres Durchschnittsnutzers ({_de_zahl(spez_gebaeude)} {mass_einheit}) "
                            f"im gleichen Zeitraum."
                        )

                chart_bars.append({
                    'color': COLORS_REFERENZ[i] if i < len(COLORS_REFERENZ) else '#ccc',
                    'year': f'{jahr} Referenz ',
                    'data': ref_monthly,
                })

    if verbrauchsart not in VERBRAUCHSARTEN_OHNE_ENERGIE_HINWEIS:
        text += '<br><br><i>Weitere Informationen zum normierten Energieverbrauch finden Sie auf unserer Website.</i>'

    return {
        'Title': title,
        'color': colorstack[0] if colorstack else '#ccc',
        'Text': text,
        'ChartType': verbrauchsart,
        'MassEinheit': mass_einheit,
        'charts': chart_bars,
    }




def get_dash_card_daten(verbrauchsart, reuid, nutzer_id):
    """
    Migrated from VerbrauchsChartDaten::getDashCardDaten().
    Returns summary data for a dashboard card.
    """
    style = DIAGRAMM_STYLE_AND_TEXT.get(verbrauchsart, DIAGRAMM_STYLE_AND_TEXT[999])
    title = style['Title']
    mass_einheit = style['MassEinheit']
    farbe = style['colorstack'][0]

    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT vd.VerBrWert, vd.VerBrSD, vd.VerBrED
            FROM tbl_v_i_sys_verbrauchs_daten vd
            LEFT JOIN tbl_v_i_sys_ver_rahmen vr ON vr.VISysVerbrauchsID = vd.VerBrID
            WHERE vr.REUID_id = %s AND vr.PersonenID_id = %s
              AND vr.VISysVerbrauchsArtID = %s
            ORDER BY vd.VerBrSD DESC
            LIMIT 1
        """, [reuid, nutzer_id, verbrauchsart])
        recent = cursor.fetchall()

    if not recent:
        return None

    verbrauchs_wert = float(recent[0][0]) if recent[0][0] is not None else 0.0
    sd = recent[0][1]
    ed = recent[0][2]

    from dateutil.relativedelta import relativedelta

    # Vormonat gezielt über das Startdatum suchen – der zweitneueste Datensatz
    # ist bei einer Lücke nicht der Vormonat.
    verbrauchs_wert_vm = 0.0
    verbrauchs_wert_vm_keine_daten = True
    if hasattr(sd, 'year'):
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT vd.VerBrWert FROM tbl_v_i_sys_verbrauchs_daten vd
                LEFT JOIN tbl_v_i_sys_ver_rahmen vr ON vr.VISysVerbrauchsID = vd.VerBrID
                WHERE vr.REUID_id = %s AND vr.PersonenID_id = %s
                  AND vr.VISysVerbrauchsArtID = %s
                  AND vd.VerBrSD = %s
                LIMIT 1
            """, [reuid, nutzer_id, verbrauchsart, sd - relativedelta(months=1)])
            vm_row = cursor.fetchone()
            if vm_row and vm_row[0] is not None:
                verbrauchs_wert_vm = float(vm_row[0])
                verbrauchs_wert_vm_keine_daten = False

    # Previous year value
    if hasattr(sd, 'year'):
        sd_vorjahr = sd - relativedelta(years=1)
        ed_vorjahr = ed - relativedelta(years=1) if ed else None
    else:
        sd_vorjahr = None
        ed_vorjahr = None

    verbrauchs_wert_vj = 0.0
    verbrauchs_wert_vj_keine_daten = True
    if sd_vorjahr and ed_vorjahr:
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT vd.VerBrWert FROM tbl_v_i_sys_verbrauchs_daten vd
                LEFT JOIN tbl_v_i_sys_ver_rahmen vr ON vr.VISysVerbrauchsID = vd.VerBrID
                WHERE vr.REUID_id = %s AND vr.PersonenID_id = %s
                  AND vr.VISysVerbrauchsArtID = %s
                  AND vd.VerBrSD = %s AND vd.VerBrED = %s
                ORDER BY vd.VerBrSD DESC LIMIT 1
            """, [reuid, nutzer_id, verbrauchsart, sd_vorjahr, ed_vorjahr])
            vj_row = cursor.fetchone()
            if vj_row and vj_row[0] is not None:
                verbrauchs_wert_vj = float(vj_row[0])
                verbrauchs_wert_vj_keine_daten = False

    # Changes
    if not verbrauchs_wert_vm_keine_daten and verbrauchs_wert_vm != 0:
        aenderung_vm = round(((verbrauchs_wert / verbrauchs_wert_vm) - 1) * 100)
    elif verbrauchs_wert_vm_keine_daten:
        aenderung_vm = ""
    else:
        aenderung_vm = "+ ∞"

    if not verbrauchs_wert_vj_keine_daten and verbrauchs_wert_vj != 0:
        aenderung_vj_raw = ((verbrauchs_wert / verbrauchs_wert_vj) - 1) * 100
        sign = '+' if aenderung_vj_raw >= 0 else ''
        aenderung_vj = f"{sign}{aenderung_vj_raw:.2f}".replace('.', ',')
    elif verbrauchs_wert_vj_keine_daten:
        aenderung_vj = ""
    else:
        aenderung_vj = "+ ∞"

    # Monat/Jahr des angezeigten Datensatzes (nicht des Vorjahres); MONATE_LANG
    # ist 0-basiert.
    monat = sd.month if hasattr(sd, 'month') else 0
    monat_name = MONATE_LANG[monat - 1] if monat else ''
    jahr = sd.year if hasattr(sd, 'year') else None

    def fmt(val):
        return f"{val:,.2f}".replace(',', 'X').replace('.', ',').replace('X', '.')

    return {
        'Title': title,
        'color': farbe,
        'Monat': monat,
        'MonatName': monat_name,
        'Jahr': jahr,
        'Verbrauchswert': fmt(verbrauchs_wert),
        'VerbrauchswertVM': fmt(verbrauchs_wert_vm),
        'VerbrauchswertVJ': fmt(verbrauchs_wert_vj),
        'VerbrauchswertVM_nv': verbrauchs_wert_vm_keine_daten,
        'VerbrauchswertVJ_nv': verbrauchs_wert_vj_keine_daten,
        'AenderungZuVM': aenderung_vm,
        'AenderungZuVJ': aenderung_vj,
        'MassEinheit': mass_einheit,
        'VerbrauchsArtID': verbrauchsart,
    }





def _rows_to_monthly_array(rows, value_col_index=0, date_col_index=1):
    """Convert DB rows to a 12-element monthly array (None for missing months)."""
    monthly = [None] * 12
    for row in rows:
        value = row[value_col_index]
        start_date = row[date_col_index]
        if start_date:
            month_num = start_date.month if hasattr(start_date, 'month') else int(str(start_date)[5:7])
            monthly[month_num - 1] = value
    return monthly


def _check_verbrauchsrahmen_exists(verbrauchsart, reuid, nutzer_id):
    """Gibt es Rahmen dieser Art fuer Einheit + Nutzer?"""
    return VISysVerbrauchRahmen.objects.filter(
        VISysVerbrauchsArtID=verbrauchsart, REUID=reuid, PersonenID=nutzer_id,
    ).exists()



def process_reuid_nutzer_id(reuid, nutzer_id, user):
    """
    Returns the tenancy (ReuMietV + joined data) or None.
    """
    try:
        qs = ReuMietV.objects.select_related(
            'REUID',        # RealEstateUnit
            'REUID__REID',  # RealEstate
        )

        if reuid == -1:
            rmv = qs.filter(MieterID=user.PersonenID).first()
        else:
            rmv = qs.filter(REUID=reuid, MieterID=nutzer_id).first()

        if not rmv:
            return None

        reu = rmv.REUID
        re  = reu.REID if reu else None

        return {
            # ReuMietV Felder
            'MietVID':           rmv.MietVID,
            'REUID':             rmv.REUID_id,
            'MieterID':          rmv.MieterID,
            'ContractValidFrom': rmv.ContractValidFrom,
            'ContractValidUntil':rmv.ContractValidUntil,
            # RealEstateUnit Felder
            'REID':    reu.REID_id if reu else None,
            'REUDesc': reu.REUDesc if reu else None,
            # RealEstate Felder
            'Street':      re.Street      if re else None,
            'HouseNumber': re.HouseNumber if re else None,
            'PostalCode':  re.PostalCode  if re else None,
            'City':        re.City        if re else None,
        }

    except Exception:
        return None



def process_mietv_id(mietvid, user):
    """
    Migrated from MietVController::processMietVID().
    Returns the tenancy (ReuMietV + joined data) or None.
    """
    with connection.cursor() as cursor:
        if mietvid == -1:
            cursor.execute("""
                SELECT rmv.*, reu.REID, reu.REUDesc, re.Street, re.HouseNumber,
                       re.PostalCode, re.City
                FROM reu_miet_v_s rmv
                LEFT JOIN real_estate_units reu ON reu.REUID = rmv.REUID
                LEFT JOIN real_estates re ON re.REID = reu.REID
                WHERE rmv.MieterID = %s
                LIMIT 1
            """, [user.PersonenID])
        else:
            cursor.execute("""
                SELECT rmv.*, reu.REID, reu.REUDesc, re.Street, re.HouseNumber,
                       re.PostalCode, re.City
                FROM reu_miet_v_s rmv
                LEFT JOIN real_estate_units reu ON reu.REUID = rmv.REUID
                LEFT JOIN real_estates re ON re.REID = reu.REID
                WHERE rmv.MietVID = %s AND rmv.MieterID = %s
                LIMIT 1
            """, [mietvid, user.PersonenID])

        columns = [col[0] for col in cursor.description]
        row = cursor.fetchone()
        if row:
            return dict(zip(columns, row))
    return None



def get_all_chart_data(reuid : int, nutzer_id : int):
    vorhandene_typen = set(
        VISysVerbrauchRahmen.objects
        .filter(PersonenID=nutzer_id, REUID=reuid)
        .values_list("VISysVerbrauchsArtID", flat=True)
        .distinct()
    )
    result = []
    for diagramm_type in DIAGRAMM_TYPES:
        if diagramm_type in vorhandene_typen:
            data = get_verbrauchs_chart_daten_neu(diagramm_type, reuid, nutzer_id)
            if data:
                result.append(data)
    return result



def get_all_chart_data_spez(reuid : int, nutzer_id : int):
    vorhandene_typen = set(
        VISysVerbrauchRahmen.objects
        .filter(PersonenID=nutzer_id, REUID=reuid)
        .values_list("VISysVerbrauchsArtID", flat=True)
        .distinct()
    )
    result = []
    # Nur die Arten mit normierter Darstellung (TitleSpez/TextSpez/NormA-Referenz);
    # 111 (WW-Volumen) hat keine und erschien sonst ohne Text und Referenz.
    for diagramm_type in DIAGRAMM_TYPES_SPEZ:
        if diagramm_type in vorhandene_typen:
            data = get_verbrauchs_chart_daten_spez(diagramm_type, reuid, nutzer_id)
            if data:
                result.append(data)
    return result


def _fetch_reu_data(mietv_id):
    """Helper: get REID and REU data for a MietVID."""
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT rmv.MietVID, rmv.REUID, reu.REID, reu.REUDesc,
                   re.Street, re.HouseNumber, re.PostalCode, re.City
            FROM reu_miet_v_s rmv
            LEFT JOIN real_estate_units reu ON reu.REUID = rmv.REUID
            LEFT JOIN real_estates re ON re.REID = reu.REID
            WHERE rmv.MietVID = %s
            LIMIT 1
        """, [mietv_id])
        columns = [col[0] for col in cursor.description]
        row = cursor.fetchone()
        if row:
            return dict(zip(columns, row))
    return None


def _fetch_verbrauchswerte(verbrauchsart, jahr, reuid, nutzer_id, field='VerBrWert'):
    # Ueber Einheit + Person, nicht ueber den Mietvertrag: Eigentuemer-Rahmen
    # haben keine MietVID.
    rahmen_ids = VISysVerbrauchRahmen.objects.filter(
        VISysVerbrauchsArtID=verbrauchsart,
        Jahr=jahr,
        REUID=reuid,
        PersonenID=nutzer_id,
    ).values_list('VISysVerbrauchsID', flat=True)

    return list(
        VISysVerbrauchsDS.objects
        .filter(VerBrID__in=rahmen_ids)
        .order_by('VerBrSD')
        .values_list(field, 'VerBrSD')
    )



def _fetch_referenzwerte(verbrauchsart, jahr, reid, field='RefVerBrWert'):
    rahmen_ids = VISysRefVerbrauchRahmen.objects.filter(
        VISysVerbrauchsArtID=verbrauchsart,
        Jahr=jahr,
        REID=reid,
    ).values_list('VISysRefVerbrID', flat=True)

    return list(
        VISysRefVerbrauchsDS.objects
        .filter(RefVerBrID__in=rahmen_ids)
        .order_by('RefVerBrSD')
        .values_list(field, 'RefVerBrSD')
    )