#!/usr/bin/env python3
"""Folie 5 – Event Sourcing: das groessere Bild (DEUTSCH).
Quellen differenziert (Publisher / Subsysteme), Event-Prozessoren als Teil des Stores,
Hot Store & Cold Store als Kernfunktion (Coral, CORE-Tag), Projektionen dazwischen,
darunter Cold Store / Monitoring / Reporting. Tech-Impls als Chips. (adesso-Styling)
Hintergrund: solides dunkles Blau (BG) – Farbe frei anpassbar."""

import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
                          F, FC, chrome)

# ---- Hintergrundfarbe (solid, keine Transparenz) – frei anpassbar ----------
BG = RGBColor(0x0A, 0x3D, 0x6B)   # dunkles Blau

prs = Presentation()
prs.slide_width  = Inches(13.333)
prs.slide_height = Inches(7.5)
SW, SH = prs.slide_width, prs.slide_height
s = prs.slides.add_slide(prs.slide_layouts[6])


def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0, adj=None):
    sh = s.shapes.add_shape(kind, x, y, w, h)
    if fill is None: sh.fill.background()
    else: sh.fill.solid(); sh.fill.fore_color.rgb = fill
    if line is None: sh.line.fill.background()
    else: sh.line.color.rgb = line; sh.line.width = lw
    if rot: sh.rotation = rot
    if adj is not None:
        try: sh.adjustments[0] = adj
        except Exception: pass
    sh.shadow.inherit = False
    return sh


def txt(x, y, w, h, t, size=16, color=WHITE, bold=False, italic=False,
        align=PP_ALIGN.LEFT, font=F, anchor=MSO_ANCHOR.MIDDLE):
    tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
    tf.word_wrap = True; tf.vertical_anchor = anchor
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
    first = True
    for line in t.split("\n"):
        p = tf.paragraphs[0] if first else tf.add_paragraph()
        first = False
        p.alignment = align
        r = p.add_run(); r.text = line
        r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic
        r.font.name = font; r.font.color.rgb = color
    return tb


def chip(cx, y, text, fill=INK, fg=WHITE, size=11):
    """Tech-Impl als kleine Pill, zentriert um cx."""
    w = Inches(0.34 + 0.092 * len(text)); h = Inches(0.30)
    x = cx - w / 2
    shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=fill, adj=0.5)
    txt(x, y, w, h, text, size=size, color=fg, bold=False,
        align=PP_ALIGN.CENTER, font=FC)


def core_tag(x, y):
    w, h = Inches(0.62), Inches(0.26)
    shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=INK, adj=0.5)
    txt(x, y, w, h, "CORE", size=9.5, color=TEAL, bold=True,
        align=PP_ALIGN.CENTER, font=FC)


# ---- solider Hintergrund (zuerst -> liegt hinten) --------------------------
bg = shp(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH, fill=BG)

chrome(s, "System Landscape", 6)

CX = Inches(6.665)            # Folienmitte (x)

# ---- Quellen: differenziert ------------------------------------------------
src_y, src_h = Inches(1.36), Inches(0.92)


def source_box(x, w, title, examples):
    shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, src_y, w, src_h, fill=WHITE, adj=0.10)
    txt(x + Inches(0.28), src_y + Inches(0.10), w - Inches(0.56), Inches(0.32),
        title, size=14.5, color=INK, bold=True, anchor=MSO_ANCHOR.TOP)
    txt(x + Inches(0.28), src_y + Inches(0.46), w - Inches(0.56), Inches(0.36),
        examples, size=12, color=MUTE, anchor=MSO_ANCHOR.TOP)


pub_x, pub_w = Inches(0.9), Inches(5.6)
sub_x, sub_w = Inches(6.83), Inches(5.6)
pub_cx = pub_x + pub_w / 2
sub_cx = sub_x + sub_w / 2

source_box(pub_x, pub_w, "Publisher  (nur publizieren)",
           "z. B. AB+M · LabAccess · MDN · Stammdaten   —  proprietär")
source_box(sub_x, sub_w, "Subsysteme  (konsumieren & publizieren)",
           "z. B. LIS · Geräte / Analyzer   —  proprietär")

# ---- Pfeile Quellen -> Hot Store ------------------------------------------
ay, ah = src_y + src_h + Inches(0.02), Inches(0.30)
shp(MSO_SHAPE.DOWN_ARROW, pub_cx - Inches(0.16), ay, Inches(0.32), ah, fill=CORAL)
txt(pub_cx + Inches(0.28), ay - Inches(0.01), Inches(1.6), ah + Inches(0.02),
    "publizieren", size=11, color=LIGHT, italic=True)
shp(MSO_SHAPE.UP_DOWN_ARROW, sub_cx - Inches(0.11), ay - Inches(0.05), Inches(0.22),
    ah + Inches(0.10), fill=CORAL)
txt(sub_cx - Inches(2.85), ay - Inches(0.01), Inches(2.5), ah + Inches(0.02),
    "konsumieren & publizieren", size=11, color=LIGHT, italic=True, align=PP_ALIGN.RIGHT)

# ---- HOT STORE (Kern) + Event-Prozessoren (Teil des Stores) ----------------
hx, hy, hw, hh = Inches(3.4), Inches(2.62), Inches(6.53), Inches(1.50)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw, hh, fill=CORAL, line=INK, lw=Pt(3.5), adj=0.07)
core_tag(hx + Inches(0.16), hy + Inches(0.13))
txt(hx, hy + Inches(0.05), hw, Inches(0.34),
    "Event Store  —  Hot Store", size=20, color=INK, bold=True, align=PP_ALIGN.CENTER)
txt(hx, hy + Inches(0.40), hw, Inches(0.20),
    "append-only · kanonische Wahrheit", size=12.5, color=INK, align=PP_ALIGN.CENTER)
chip(CX, hy + Inches(0.62), "z. B. Kafka")

# Event-Prozessoren als eingebetteter Bestandteil des Stores
ep_x, ep_y, ep_w, ep_h = hx + Inches(0.28), hy + Inches(0.98), hw - Inches(0.56), Inches(0.44)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, ep_x, ep_y, ep_w, ep_h, fill=WHITE, adj=0.22)
txt(ep_x, ep_y, ep_w, ep_h,
    "Event-Prozessoren  (konsumieren & publizieren)  ·  z. B. Proben-Routing",
    size=11.5, color=INK, bold=True, align=PP_ALIGN.CENTER)

# ---- Hot -> Projektionen ---------------------------------------------------
shp(MSO_SHAPE.DOWN_ARROW, CX - Inches(0.16), hy + hh + Inches(0.03),
    Inches(0.32), Inches(0.28), fill=CORAL)

# ---- PROJEKTIONEN ----------------------------------------------------------
px, py, pw, ph = Inches(3.4), Inches(4.46), Inches(6.53), Inches(0.66)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, px, py, pw, ph, fill=WHITE, adj=0.14)
txt(px + Inches(0.3), py, Inches(3.0), ph, "Projektionen", size=16, color=INK, bold=True)
chip(px + pw - Inches(2.0), py + ph / 2 - Inches(0.15), "Kafka Streams → PostgreSQL")

# ---- Verteiler (Projektionen -> 3 Bloecke) --------------------------------
boxes_y = Inches(5.34)
bw = Inches(3.7); bh = Inches(1.56)
cold_x = Inches(0.9);   cold_cx = cold_x + bw / 2
mon_x  = Inches(4.815); mon_cx  = mon_x + bw / 2
rep_x  = Inches(8.73);  rep_cx  = rep_x + bw / 2

bus_y = py + ph + Inches(0.10)
shp(MSO_SHAPE.RECTANGLE, CX - Pt(0.75), py + ph, Pt(1.5), bus_y - (py + ph), fill=LIGHT)
shp(MSO_SHAPE.RECTANGLE, cold_cx, bus_y, rep_cx - cold_cx, Pt(1.5), fill=LIGHT)
for cx in (cold_cx, mon_cx, rep_cx):
    shp(MSO_SHAPE.DOWN_ARROW, cx - Inches(0.11), bus_y, Inches(0.22), boxes_y - bus_y,
        fill=LIGHT)


def block(x, title, body, tech, core=False):
    fill = CORAL if core else WHITE
    line = INK if core else None
    lw   = Pt(3.5) if core else Pt(1)
    shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, boxes_y, bw, bh, fill=fill, line=line, lw=lw, adj=0.07)
    ty = boxes_y + (Inches(0.30) if core else Inches(0.20))
    if core:
        core_tag(x + Inches(0.16), boxes_y + Inches(0.16))
    txt(x, ty, bw, Inches(0.42), title, size=17, color=INK, bold=True, align=PP_ALIGN.CENTER)
    txt(x + Inches(0.25), ty + Inches(0.46), bw - Inches(0.5), Inches(0.5), body,
        size=12.5, color=(INK if core else MUTE), align=PP_ALIGN.CENTER)
    chip(x + bw / 2, boxes_y + bh - Inches(0.42), tech)


block(cold_x, "Cold Store",
      "Archiv · Audit-Trail · Dokumente", "z. B. PostgreSQL", core=True)
block(mon_x, "Monitoring",
      "Auftragsstatus / Auslastung", "z. B. Grafana")
block(rep_x, "Reporting",
      "Bericht an Gruppe · KPIs", "z. B. Power BI")

out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie5.pptx")
prs.save(out)
print("saved", out)
