// Nachrichten — kontextstart, vorlagen mit auto-fill, geteilter posteingang, lesebestätigungen

// ───── Eigentümer (Team) ─────
const OWNERS = [
  { id: "o1", name: "Marie Tessmann", initials: "MT", role: "Eigentümer", color: "#2d4a3e" },
  { id: "o2", name: "Stefan Tessmann", initials: "ST", role: "Mit-Eigentümer", color: "#7a5a2e" },
  { id: "o3", name: "Lisa Voigt", initials: "LV", role: "Verwalterin", color: "#5a4a7a" },
];
const ME = "o1"; // current user

// ───── Vorlagen ─────
// Each template: id, name, category, channel, subject, build({tenant, unit, property, ctx}) -> body
const TEMPLATES = [
  {
    id: "rent_increase",
    name: "Mieterhöhung (Indexmiete)",
    category: "Vertrag",
    icon: "↗",
    channel: "email",
    subject: "Mietanpassung gemäß Indexmiete",
    fields: [
      { id: "newRent", label: "Neue Kaltmiete", type: "currency", default: (ctx) => ctx.unit ? Math.round(ctx.unit.rent * 1.038) : 0 },
      { id: "fromDate", label: "Gültig ab", type: "date", default: () => "2026-09-01" },
      { id: "indexPct", label: "Indexsteigerung", type: "percent", default: () => "3,8" },
    ],
    build: ({ tenant, unit, property, fields }) => {
      const oldRent = unit?.rent || 0;
      const newRent = fields.newRent || 0;
      const diff = newRent - oldRent;
      const pct = fields.indexPct || "3,8";
      return `Sehr geehrte/r ${tenant?.name || "Mieter/in"},

gemäß §557b BGB und der vereinbarten Indexmiete-Klausel in Ihrem Mietvertrag passen wir die Kaltmiete für ${unit?.label || "Ihre Wohnung"} (${property?.address || ""}) wie folgt an:

   Bisherige Kaltmiete:    ${oldRent.toLocaleString('de-DE')} €
   Neue Kaltmiete:         ${newRent.toLocaleString('de-DE')} €
   Erhöhung:               ${diff > 0 ? "+" : ""}${diff.toLocaleString('de-DE')} €  (${pct} %)
   Gültig ab:              ${fmtDateLong(fields.fromDate)}

Grundlage: Verbraucherpreisindex des Statistischen Bundesamts. Die Berechnung finden Sie im Anhang.

Bitte passen Sie Ihren Dauerauftrag rechtzeitig an. Bei Rückfragen melden Sie sich gern.

Mit freundlichen Grüßen
Marie Tessmann`;
    },
  },
  {
    id: "appointment",
    name: "Terminvorschlag",
    category: "Termin",
    icon: "📅",
    channel: "email",
    subject: "Terminvorschlag",
    fields: [
      { id: "reason", label: "Anlass", type: "text", default: () => "Wohnungsbesichtigung" },
      { id: "date", label: "Datum", type: "date", default: () => "2026-05-12" },
      { id: "time", label: "Uhrzeit", type: "text", default: () => "14:00 – 15:00 Uhr" },
    ],
    build: ({ tenant, unit, property, fields }) => `Hallo ${firstName(tenant?.name)},

ich würde gern einen Termin mit Ihnen vereinbaren — Anlass: ${fields.reason}.

Mein Vorschlag:
   ${fmtDateLong(fields.date)} · ${fields.time}
   ${property?.address || ""}${unit ? ", " + unit.label : ""}

Passt das? Falls nicht, schlagen Sie gern eine Alternative vor.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "dunning",
    name: "Mahnung Mietrückstand",
    category: "Forderung",
    icon: "!",
    channel: "email",
    subject: "Mietrückstand — Zahlungserinnerung",
    fields: [
      { id: "amount", label: "Offener Betrag", type: "currency", default: (ctx) => ctx.unit?.rent || 0 },
      { id: "month", label: "Betroffener Monat", type: "text", default: () => "Mai 2026" },
      { id: "deadline", label: "Zahlungsfrist", type: "date", default: () => "2026-05-15" },
    ],
    build: ({ tenant, unit, fields }) => `Sehr geehrte/r ${tenant?.name || "Mieter/in"},

unsere Buchhaltung verzeichnet für ${fields.month} einen Mietrückstand für ${unit?.label || ""} in Höhe von ${(fields.amount || 0).toLocaleString('de-DE')} €.

Bitte überweisen Sie den Betrag bis spätestens ${fmtDateLong(fields.deadline)} auf das bekannte Konto.

Sollte die Zahlung bereits erfolgt sein, betrachten Sie dieses Schreiben als gegenstandslos. Bei Liquiditätsproblemen melden Sie sich bitte umgehend — wir finden gemeinsam eine Lösung.

Mit freundlichen Grüßen
Marie Tessmann`,
  },
  {
    id: "maintenance_confirm",
    name: "Wartungstermin bestätigt",
    category: "Wartung",
    icon: "🔧",
    channel: "email",
    subject: "Wartungstermin",
    fields: [
      { id: "issue", label: "Mangel/Arbeit", type: "text", default: (ctx) => ctx.maintenanceTitle || "Reparatur" },
      { id: "tradesperson", label: "Handwerker", type: "text", default: () => "Klempnerei Schmidt" },
      { id: "date", label: "Termin", type: "date", default: () => "2026-05-08" },
      { id: "window", label: "Zeitfenster", type: "text", default: () => "10:00 – 12:00 Uhr" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

danke für Ihre Meldung. Ich habe einen Termin für „${fields.issue}" organisiert:

   ${fields.tradesperson}
   ${fmtDateLong(fields.date)} · ${fields.window}

Bitte stellen Sie sicher, dass jemand zuhause ist oder hinterlegen Sie den Schlüssel. Der Handwerker meldet sich vorher kurz telefonisch.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "nk_settlement",
    name: "Nebenkostenabrechnung",
    category: "Abrechnung",
    icon: "Σ",
    channel: "email",
    subject: "Nebenkostenabrechnung — Ihr Saldo",
    fields: [
      { id: "year", label: "Abrechnungsjahr", type: "text", default: () => "2025" },
      { id: "balance", label: "Saldo (− = Guthaben)", type: "currency", default: () => -148 },
    ],
    build: ({ tenant, unit, fields }) => {
      const bal = fields.balance || 0;
      const isCredit = bal < 0;
      return `Sehr geehrte/r ${tenant?.name || "Mieter/in"},

anbei die Nebenkostenabrechnung für ${unit?.label || "Ihre Wohnung"} für das Jahr ${fields.year}.

   Ihr Saldo:  ${isCredit ? "Guthaben " : "Nachzahlung "}${Math.abs(bal).toLocaleString('de-DE')} €

${isCredit
        ? "Den Betrag erhalten Sie in den nächsten 14 Tagen auf Ihr Konto erstattet."
        : "Bitte überweisen Sie den Betrag innerhalb von 30 Tagen."}

Eine detaillierte Aufstellung finden Sie im Anhang.

Mit freundlichen Grüßen
Marie Tessmann`;
    },
  },
  {
    id: "modernization",
    name: "Modernisierungs-Ankündigung",
    category: "Vertrag",
    icon: "🏗",
    channel: "email",
    subject: "Modernisierungsmaßnahme — Ankündigung",
    fields: [
      { id: "measure", label: "Maßnahme", type: "text", default: () => "Fassadendämmung" },
      { id: "from", label: "Beginn", type: "date", default: () => "2026-08-01" },
      { id: "to", label: "Ende", type: "date", default: () => "2026-09-30" },
    ],
    build: ({ tenant, property, fields }) => `Sehr geehrte/r ${tenant?.name || "Mieter/in"},

hiermit kündigen wir gemäß §555c BGB die folgende Modernisierungsmaßnahme im Objekt ${property?.address || ""} an:

   Maßnahme:    ${fields.measure}
   Zeitraum:    ${fmtDateLong(fields.from)} bis ${fmtDateLong(fields.to)}

Eine ausführliche Beschreibung der Arbeiten, der voraussichtlichen Belastungen und der zu erwartenden Mietanpassung finden Sie im Anhang.

Bei Rückfragen stehe ich Ihnen gern zur Verfügung.

Mit freundlichen Grüßen
Marie Tessmann`,
  },

  // ───── Hausmeister / Objekt-weite Maßnahmen (oft Rundmail) ─────
  {
    id: "water_shutoff",
    name: "Wasserabstellung",
    category: "Hausmeister",
    icon: "💧",
    channel: "email",
    bulkFriendly: true,
    subject: "Wasserabstellung im Haus",
    fields: [
      { id: "date", label: "Datum", type: "date", default: () => "2026-05-14" },
      { id: "from", label: "Von", type: "text", default: () => "08:00 Uhr" },
      { id: "to", label: "Bis (ca.)", type: "text", default: () => "13:00 Uhr" },
      { id: "reason", label: "Grund", type: "text", default: () => "Reparatur Hauptleitung" },
    ],
    build: ({ tenant, property, fields }) => `Hallo ${firstName(tenant?.name)},

aufgrund von Arbeiten (${fields.reason}) wird das Wasser im Objekt ${property?.address || ""} abgestellt:

   ${fmtDateLong(fields.date)} · ${fields.from} – ${fields.to}

Bitte rechtzeitig Wasser abkochen oder bevorraten. Nach Wiederinbetriebnahme empfiehlt sich, kurz die Hähne ablaufen zu lassen.

Vielen Dank für Ihr Verständnis.
Marie Tessmann`,
  },
  {
    id: "stairwell_cleaning",
    name: "Treppenhausreinigung",
    category: "Hausmeister",
    icon: "🧹",
    channel: "email",
    bulkFriendly: true,
    subject: "Treppenhausreinigung — Hinweis",
    fields: [
      { id: "weekday", label: "Wochentag", type: "text", default: () => "Donnerstag" },
      { id: "time", label: "Uhrzeit", type: "text", default: () => "ab 09:00 Uhr" },
      { id: "company", label: "Reinigungsfirma", type: "text", default: () => "Sauberwerk GmbH" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

ab sofort übernimmt ${fields.company} die wöchentliche Treppenhausreinigung — jeden ${fields.weekday}, ${fields.time}.

Bitte stellen Sie bis dahin Schuhe, Fußmatten und persönliche Gegenstände aus dem Hausflur. Die Kosten sind Bestandteil der Nebenkostenabrechnung.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "chimney_sweep",
    name: "Schornsteinfeger-Termin",
    category: "Hausmeister",
    icon: "🔥",
    channel: "email",
    bulkFriendly: true,
    subject: "Schornsteinfeger — Pflichttermin",
    fields: [
      { id: "date", label: "Datum", type: "date", default: () => "2026-05-20" },
      { id: "window", label: "Zeitfenster", type: "text", default: () => "10:00 – 14:00 Uhr" },
      { id: "name", label: "Schornsteinfeger", type: "text", default: () => "Bezirksschornsteinfeger Krause" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

der Schornsteinfeger (${fields.name}) führt die jährliche Pflichtüberprüfung durch:

   ${fmtDateLong(fields.date)} · ${fields.window}

Bitte sorgen Sie dafür, dass die Wohnung zugänglich ist (jemand zuhause oder Schlüssel hinterlegt). Der Termin ist gesetzlich verpflichtend (§1 SchfHwG).

Vielen Dank.
Marie Tessmann`,
  },
  {
    id: "smoke_detector",
    name: "Rauchmelder-Wartung",
    category: "Hausmeister",
    icon: "🚨",
    channel: "email",
    bulkFriendly: true,
    subject: "Jährliche Rauchmelder-Wartung",
    fields: [
      { id: "date", label: "Datum", type: "date", default: () => "2026-06-04" },
      { id: "window", label: "Zeitfenster", type: "text", default: () => "14:00 – 17:00 Uhr" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

die DIN 14676 schreibt eine jährliche Funktionsprüfung der Rauchmelder vor. Termin in Ihrer Wohnung:

   ${fmtDateLong(fields.date)} · ${fields.window}

Sollten Sie verhindert sein, melden Sie sich bitte für eine Alternative — der Termin ist verpflichtend.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "winter_service",
    name: "Winterdienst-Hinweis",
    category: "Hausmeister",
    icon: "❄",
    channel: "email",
    bulkFriendly: true,
    subject: "Winterdienst & Streupflicht",
    fields: [
      { id: "season", label: "Saison", type: "text", default: () => "November 2026 – März 2027" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

zur Erinnerung — in der Saison ${fields.season} gilt im Objekt der Streu- und Räumplan im Hausflur. Werktags 7:00 – 20:00 Uhr, Sonn- und Feiertags 9:00 – 20:00 Uhr.

Bitte halten Sie Ihre Tage ein. Bei Verhinderung organisieren Sie bitte einen Tausch mit einem Nachbarn.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "garbage_calendar",
    name: "Müllkalender-Hinweis",
    category: "Hausmeister",
    icon: "🗑",
    channel: "email",
    bulkFriendly: true,
    subject: "Müllabfuhr-Änderung",
    fields: [
      { id: "wasteType", label: "Abfallart", type: "text", default: () => "Gelbe Tonne" },
      { id: "newDay", label: "Neuer Abholtag", type: "text", default: () => "Mittwoch (alle 14 Tage, ungerade KW)" },
      { id: "fromDate", label: "Gültig ab", type: "date", default: () => "2026-06-01" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

ab ${fmtDateLong(fields.fromDate)} ändert sich die Abholung der ${fields.wasteType}: ${fields.newDay}.

Bitte stellen Sie die Tonne am Vorabend bis 7:00 Uhr bereit.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "house_rules",
    name: "Hausordnung-Erinnerung",
    category: "Hausmeister",
    icon: "📜",
    channel: "email",
    bulkFriendly: true,
    subject: "Erinnerung: Hausordnung",
    fields: [
      { id: "topic", label: "Anlass", type: "text", default: () => "Ruhezeiten und Fahrradstellplätze" },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

ich möchte freundlich an die Hausordnung erinnern, insbesondere zum Thema ${fields.topic}.

Ruhezeiten gelten werktags 22:00 – 7:00 Uhr und durchgehend an Sonn- und Feiertagen. Fahrräder gehören in den Fahrradkeller, nicht ins Treppenhaus (Brandschutz).

Vielen Dank für die Rücksichtnahme — eine gute Nachbarschaft macht das Wohnen für alle angenehmer.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "garden_service",
    name: "Gartenpflege-Termin",
    category: "Hausmeister",
    icon: "🌿",
    channel: "email",
    bulkFriendly: true,
    subject: "Gartenpflege im Haus",
    fields: [
      { id: "date", label: "Datum", type: "date", default: () => "2026-05-22" },
      { id: "company", label: "Gärtner", type: "text", default: () => "Grünwerk Stahl" },
    ],
    build: ({ tenant, fields, property }) => `Hallo ${firstName(tenant?.name)},

am ${fmtDateLong(fields.date)} kümmert sich ${fields.company} um den Garten / die Außenanlagen von ${property?.address || ""}.

Bitte räumen Sie persönliche Gegenstände (Stühle, Spielzeug, Wäsche) aus den Außenbereichen.

Viele Grüße
Marie Tessmann`,
  },

  // ───── Mieter-Service ─────
  {
    id: "key_lost",
    name: "Antwort: Schlüsselverlust",
    category: "Mieter-Service",
    icon: "🔑",
    channel: "email",
    subject: "Schlüsselverlust",
    fields: [
      { id: "newKeyCost", label: "Neue Schlüssel", type: "currency", default: () => 85 },
      { id: "lockChange", label: "Zylindertausch (falls nötig)", type: "currency", default: () => 280 },
    ],
    build: ({ tenant, fields }) => `Hallo ${firstName(tenant?.name)},

danke für Ihre umgehende Meldung. Vorgehen:

1. Wir lassen ${(fields.newKeyCost || 0).toLocaleString('de-DE')} € für einen Ersatzschlüssel anfertigen.
2. Falls der Schlüssel nicht aufgefunden wird, tauschen wir aus Sicherheitsgründen den Zylinder (~${(fields.lockChange || 0).toLocaleString('de-DE')} €).
3. Die Kosten gehen zu Ihren Lasten — bitte prüfen Sie, ob Ihre Hausratversicherung den Verlust abdeckt.

Bitte bestätigen Sie kurz das Vorgehen.

Viele Grüße
Marie Tessmann`,
  },
  {
    id: "owner_change",
    name: "Eigentümerwechsel",
    category: "Vertrag",
    icon: "↻",
    channel: "email",
    bulkFriendly: true,
    subject: "Wichtige Information — Eigentümerwechsel",
    fields: [
      { id: "fromDate", label: "Ab Datum", type: "date", default: () => "2026-07-01" },
      { id: "newOwner", label: "Neuer Eigentümer", type: "text", default: () => "Tessmann Immobilien GmbH" },
      { id: "newIban", label: "Neue IBAN", type: "text", default: () => "DE12 3456 7890 1234 5678 90" },
    ],
    build: ({ tenant, property, unit, fields }) => `Sehr geehrte/r ${tenant?.name || "Mieter/in"},

hiermit informieren wir Sie, dass das Objekt ${property?.address || ""} ab ${fmtDateLong(fields.fromDate)} in das Eigentum von ${fields.newOwner} übergeht.

Ihr Mietvertrag bleibt unverändert bestehen (§566 BGB — „Kauf bricht nicht Miete"). Ab dem genannten Datum überweisen Sie die Miete bitte auf folgendes Konto:

   ${fields.newOwner}
   IBAN: ${fields.newIban}
   Verwendungszweck: ${property?.name || ""}${unit?.label ? `, ${unit.label}` : ""}, Mieter ${tenant?.name || ""}

Bitte passen Sie Ihren Dauerauftrag rechtzeitig an.

Mit freundlichen Grüßen
Marie Tessmann`,
  },
];

function firstName(name) {
  if (!name) return "";
  if (name.startsWith("Familie")) return name;
  if (name.startsWith("Dr.")) return name.split(" ").slice(0, 2).join(" ");
  return name.split(" ")[0];
}
function fmtDateLong(s) {
  if (!s) return "";
  const d = new Date(s);
  return d.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });
}

// ───── Threads (mit assignees + read receipts) ─────
// Leer in Production — Threads entstehen erst durch echte Mieter-Kommunikation.
const INITIAL_THREADS = [];
const INITIAL_THREADS_DEMO_KEEP = [
  {
    id: "th1", with: "Anna Hofmann", tenantId: "t1", role: "Mieter", propertyId: "p1", unitId: "u1",
    channel: "email", unread: 2, lastDate: "2026-04-30T14:24",
    needsReply: true, assignedTo: "o1",
    messages: [
      { id: 1, from: "tenant", date: "2026-04-30T14:22", body: "Guten Tag Frau Tessmann, in der Küche tropft seit gestern der Wasserhahn — könnte jemand vorbeischauen? Mit freundlichen Grüßen, Anna Hofmann" },
      { id: 2, from: "tenant", date: "2026-04-30T14:24", body: "Anbei ein Foto.", attachment: "wasserhahn_tropft.jpg" },
    ]
  },
  {
    id: "th2", with: "Café Mira GbR", tenantId: "t12", role: "Pächter", propertyId: "p4", unitId: "u13",
    channel: "email", unread: 0, lastDate: "2026-04-29T09:14",
    needsReply: false, assignedTo: "o2",
    messages: [
      { id: 1, from: "tenant", date: "2026-04-28T17:10", body: "Hallo, die Indexmiete-Anpassung ab September — könnten wir das einmal kurz telefonisch besprechen?",
        receipt: { sentBy: null } },
      { id: 2, from: "owner", senderId: "o2", date: "2026-04-29T09:14", body: "Gerne, ich rufe morgen zwischen 10 und 12 an.",
        receipt: { sent: "2026-04-29T09:14", delivered: "2026-04-29T09:14:08", read: "2026-04-29T09:18" } },
    ]
  },
  {
    id: "th3", with: "Familie Schulz", tenantId: "t3", role: "Mieter", propertyId: "p1", unitId: "u3",
    channel: "chat", unread: 1, lastDate: "2026-04-30T08:45",
    needsReply: true, assignedTo: null,
    messages: [
      { id: 1, from: "tenant", date: "2026-04-30T08:45", body: "Können wir am Samstag den Keller besichtigen? Wir wollten alte Kartons aussortieren." },
    ]
  },
  {
    id: "th4", with: "Tobias Lang", tenantId: "t5", role: "Mieter", propertyId: "p2", unitId: "u6",
    channel: "email", unread: 0, lastDate: "2026-04-25T11:00",
    needsReply: false, assignedTo: "o1",
    messages: [
      { id: 1, from: "owner", senderId: "o1", date: "2026-04-22T10:00", body: "Modernisierungs-Ankündigung Fassade — siehe Anhang.", attachment: "Modernisierung_Stadtpark7.pdf",
        receipt: { sent: "2026-04-22T10:00", delivered: "2026-04-22T10:00:15", read: "2026-04-22T10:42" } },
      { id: 2, from: "tenant", date: "2026-04-25T11:00", body: "Verstanden, danke für die frühzeitige Info." },
    ]
  },
  {
    id: "th5", with: "Jens Maier", tenantId: "t7", role: "Mieter", propertyId: "p2", unitId: "u8",
    channel: "email", unread: 1, lastDate: "2026-04-28T16:30",
    needsReply: true, assignedTo: "o3",
    messages: [
      { id: 1, from: "owner", senderId: "o1", date: "2026-04-12T09:00", body: "Bestätigung Ihrer Kündigung zum 31.07.2026.",
        receipt: { sent: "2026-04-12T09:00", delivered: "2026-04-12T09:00:12", read: "2026-04-12T11:08" } },
      { id: 2, from: "tenant", date: "2026-04-28T16:30", body: "Können wir die Übergabe schon auf den 25.07. vorziehen? Danke." },
    ]
  },
  {
    id: "th6", with: "Lukas Wolf", tenantId: "t11", role: "Mieter", propertyId: "p4", unitId: "u12",
    channel: "chat", unread: 0, lastDate: "2026-04-26T19:12",
    needsReply: false, assignedTo: "o1",
    messages: [
      { id: 1, from: "owner", senderId: "o1", date: "2026-04-26T19:00", body: "Mahnung: 980 € Mietrückstand — bitte bis 15.05. überweisen.",
        receipt: { sent: "2026-04-26T19:00", delivered: "2026-04-26T19:00:04", read: "2026-04-26T19:11" } },
      { id: 2, from: "tenant", date: "2026-04-26T19:12", body: "Wird Anfang Mai überwiesen, sorry." },
    ]
  },
];

// ───── State Store ─────
const MessagesContext = React.createContext(null);

function MessagesProvider({ children }) {
  const [threads, setThreads] = React.useState(INITIAL_THREADS);
  // composer prefill: { threadId?, tenantId?, propertyId?, unitId?, templateId?, ctx? }
  const [pendingCompose, setPendingCompose] = React.useState(null);

  const value = {
    threads, setThreads,
    pendingCompose, setPendingCompose,
    appendMessage: (threadId, msg) => {
      setThreads(prev => prev.map(t => t.id === threadId
        ? { ...t, messages: [...t.messages, msg], lastDate: msg.date, needsReply: false }
        : t));
    },
    markRead: (threadId) => {
      setThreads(prev => prev.map(t => t.id === threadId ? { ...t, unread: 0 } : t));
    },
    assignThread: (threadId, ownerId) => {
      setThreads(prev => prev.map(t => t.id === threadId ? { ...t, assignedTo: ownerId } : t));
    },
  };
  return <MessagesContext.Provider value={value}>{children}</MessagesContext.Provider>;
}
const useMessages = () => React.useContext(MessagesContext);

// ───── Notification bell ─────
function NotificationBell({ setRoute }) {
  const ctx = useMessages();
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  const threads = ctx?.threads || INITIAL_THREADS;
  const unread = threads.reduce((s, t) => s + t.unread, 0);
  const needsReply = threads.filter(t => t.needsReply).length;

  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);

  const unreadThreads = threads.filter(t => t.unread > 0).slice(0, 4);
  const items = [
    ...unreadThreads.map(t => ({
      type: 'message', threadId: t.id,
      title: `${t.with} · ${t.messages[t.messages.length - 1].body.slice(0, 60)}`,
      when: fmtTime(t.lastDate),
      urgent: t.needsReply,
    })),
    { type: 'task', title: '3 Belege fehlen für Lindenstraße', when: 'heute', urgent: true },
    { type: 'deadline', title: 'Nebenkostenabrechnung in 18 Tagen', when: 'fällig 19.05.' },
  ];

  return (
    <div className="bell-wrap" ref={ref}>
      <button className="bell-btn" onClick={() => setOpen(o => !o)} aria-label="Benachrichtigungen">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
          <path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9" />
          <path d="M13.7 21a2 2 0 0 1-3.4 0" />
        </svg>
        {unread > 0 && <span className="bell-dot">{unread}</span>}
      </button>
      {open && (
        <div className="bell-pop">
          <div className="bell-pop-head">
            <div>
              <div className="bell-pop-title">Posteingang</div>
              <div className="bell-pop-sub">{unread} ungelesen · {needsReply} offene Antworten</div>
            </div>
            <button className="btn-link" onClick={() => { setOpen(false); setRoute({ page: 'messages' }); }}>Alle öffnen →</button>
          </div>
          <div className="bell-pop-list">
            {items.map((n, i) => (
              <button key={i} className="bell-pop-item"
                onClick={() => { setOpen(false); if (n.threadId) setRoute({ page: 'messages', threadId: n.threadId }); else setRoute({ page: 'messages' }); }}>
                <span className={`bell-pop-icon ${n.type}`}>
                  {n.type === 'message' && '✉'}
                  {n.type === 'task' && '!'}
                  {n.type === 'deadline' && '⏱'}
                </span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className={`bell-pop-item-title ${n.urgent ? 'urgent' : ''}`}>{n.title}</div>
                  <div className="bell-pop-item-meta">{n.when}</div>
                </div>
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ───── Helper: button to start a message from any context ─────
function StartMessageButton({ tenantId, propertyId, unitId, maintenanceId, label, className, setRoute, data }) {
  const ctx = useMessages();
  return (
    <button
      className={className || "btn btn-sm"}
      onClick={() => {
        // Try find existing thread with this tenant
        const existing = ctx.threads.find(t => t.tenantId === tenantId);
        const tenant = data?.tenants?.[tenantId];
        const property = data?.properties?.find(p => p.id === propertyId);
        const unit = property?.units.find(u => u.id === unitId);
        const maintenance = maintenanceId
          ? data?.maintenance?.find(m => m.id === maintenanceId)
          : null;

        if (existing) {
          ctx.setPendingCompose({
            threadId: existing.id,
            templateId: maintenance ? "maintenance_confirm" : null,
            ctx: { maintenanceTitle: maintenance?.title }
          });
          setRoute({ page: 'messages', threadId: existing.id });
        } else if (tenant) {
          // create new draft thread
          const newThread = {
            id: `th_new_${Date.now()}`,
            with: tenant.name,
            tenantId,
            role: "Mieter",
            propertyId, unitId,
            channel: "email",
            unread: 0,
            lastDate: new Date().toISOString(),
            needsReply: false,
            assignedTo: ME,
            messages: [],
            isDraft: true,
          };
          ctx.setThreads(prev => [newThread, ...prev]);
          ctx.setPendingCompose({
            threadId: newThread.id,
            templateId: maintenance ? "maintenance_confirm" : null,
            ctx: { maintenanceTitle: maintenance?.title }
          });
          setRoute({ page: 'messages', threadId: newThread.id });
        }
      }}
    >
      {label || "✉ Nachricht"}
    </button>
  );
}

// ───── Read receipt indicator ─────
function ReadReceipt({ receipt, channel }) {
  if (!receipt) return null;
  const { sent, delivered, read } = receipt;
  let state = 'sent';
  if (read) state = 'read';
  else if (delivered) state = 'delivered';

  const labels = {
    sent: 'gesendet',
    delivered: 'zugestellt',
    read: channel === 'email' ? 'geöffnet' : 'gelesen',
  };
  const ts = read || delivered || sent;

  return (
    <span className={`read-receipt ${state}`} title={`${labels[state]} · ${ts ? new Date(ts).toLocaleString('de-DE') : ''}`}>
      {state === 'sent' && (
        <svg width="14" height="10" viewBox="0 0 14 10" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M1 5l3 3 6-6" strokeLinecap="round" strokeLinejoin="round"/></svg>
      )}
      {state === 'delivered' && (
        <svg width="18" height="10" viewBox="0 0 18 10" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M1 5l3 3 6-6M7 8l1 0 6-6" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      )}
      {state === 'read' && (
        <svg width="18" height="10" viewBox="0 0 18 10" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M1 5l3 3 6-6M7 8l1 0 6-6" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      )}
      <span className="read-receipt-label">{labels[state]}</span>
    </span>
  );
}

// ───── Broadcast composer (Rundmail an viele Mieter) ─────
function BroadcastModal({ open, onClose, data }) {
  const ctx = useMessages();
  const [step, setStep] = React.useState(1); // 1=template, 2=recipients, 3=preview
  const [tpl, setTpl] = React.useState(null);
  const [tmplFields, setTmplFields] = React.useState({});
  const [propertyFilter, setPropertyFilter] = React.useState('all'); // 'all' | propertyId
  const [selected, setSelected] = React.useState(new Set()); // tenantIds
  const [sending, setSending] = React.useState(false);
  const [sent, setSent] = React.useState(0);

  React.useEffect(() => {
    if (!open) {
      setStep(1); setTpl(null); setTmplFields({});
      setPropertyFilter('all'); setSelected(new Set());
      setSending(false); setSent(0);
    }
  }, [open]);

  if (!open) return null;

  const bulkTemplates = TEMPLATES.filter(t => t.bulkFriendly);
  const cats = [...new Set(bulkTemplates.map(t => t.category))];

  // Build recipient pool: all active tenants
  const allTenants = Object.values(data.tenants).filter(t => t.status !== 'former');
  const filteredTenants = propertyFilter === 'all'
    ? allTenants
    : allTenants.filter(t => t.propertyId === propertyFilter);

  function pickTemplate(t) {
    setTpl(t);
    const init = {};
    t.fields.forEach(f => {
      const def = typeof f.default === 'function' ? f.default({}) : f.default;
      init[f.id] = def;
    });
    setTmplFields(init);
    // pre-select all tenants
    setSelected(new Set(allTenants.map(t => t.id)));
    setStep(2);
  }

  function toggle(id) {
    const s = new Set(selected);
    s.has(id) ? s.delete(id) : s.add(id);
    setSelected(s);
  }
  function selectAll(yes) {
    setSelected(yes ? new Set(filteredTenants.map(t => t.id)) : new Set());
  }

  async function doSend() {
    setSending(true);
    const recipients = allTenants.filter(t => selected.has(t.id));
    let count = 0;
    for (const tenant of recipients) {
      const property = data.properties.find(p => p.id === tenant.propertyId);
      const unit = property?.units.find(u => u.id === tenant.unitId);
      const body = tpl.build({ tenant, property, unit, fields: tmplFields });
      const now = new Date();
      const msg = {
        id: Date.now() + count,
        from: 'owner',
        senderId: ME,
        date: now.toISOString(),
        body,
        receipt: { sent: now.toISOString(), delivered: null, read: null },
      };
      // find existing thread or create new
      const existing = ctx.threads.find(th => th.tenantId === tenant.id);
      if (existing) {
        ctx.appendMessage(existing.id, msg);
      } else {
        const newThread = {
          id: `th_bc_${Date.now()}_${count}`,
          with: tenant.name,
          tenantId: tenant.id,
          role: "Mieter",
          propertyId: tenant.propertyId,
          unitId: tenant.unitId,
          channel: "email",
          unread: 0,
          lastDate: now.toISOString(),
          needsReply: false,
          assignedTo: ME,
          messages: [msg],
        };
        ctx.setThreads(prev => [newThread, ...prev]);
      }
      count++;
      setSent(count);
      // small delay for visual progress
      await new Promise(r => setTimeout(r, 60));
    }
    // simulate delivery + read receipts after a moment
    setTimeout(() => {
      ctx.setThreads(prev => prev.map(th => ({
        ...th,
        messages: th.messages.map(m => m.senderId === ME && m.receipt && !m.receipt.delivered && Date.now() - new Date(m.receipt.sent).getTime() > 1000
          ? { ...m, receipt: { ...m.receipt, delivered: new Date().toISOString() } }
          : m)
      })));
    }, 1500);
    setTimeout(() => onClose(), 1200);
  }

  // Step 1: template
  if (step === 1) {
    return (
      <div className="modal-backdrop" onClick={onClose}>
        <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 580 }}>
          <div className="modal-head">
            <div>
              <div className="modal-title">📢 Rundmail verfassen</div>
              <div className="modal-sub">Schritt 1 von 3 — Vorlage auswählen</div>
            </div>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div className="tmpl-list">
            {cats.map(cat => (
              <div key={cat} className="tmpl-cat">
                <div className="tmpl-cat-label">{cat}</div>
                {bulkTemplates.filter(t => t.category === cat).map(t => (
                  <button key={t.id} className="tmpl-row" onClick={() => pickTemplate(t)}>
                    <span className="tmpl-icon">{t.icon}</span>
                    <span className="tmpl-name">{t.name}</span>
                    <span className="tmpl-arrow">→</span>
                  </button>
                ))}
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // Step 2: recipients
  if (step === 2) {
    const allInFilterSelected = filteredTenants.every(t => selected.has(t.id)) && filteredTenants.length > 0;
    return (
      <div className="modal-backdrop" onClick={onClose}>
        <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 680, maxHeight: '85vh' }}>
          <div className="modal-head">
            <div>
              <div className="modal-title">📢 Rundmail · {tpl.name}</div>
              <div className="modal-sub">Schritt 2 von 3 — Empfänger auswählen ({selected.size} ausgewählt)</div>
            </div>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div style={{ padding: '14px 22px 0', display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
            <select className="bc-select" value={propertyFilter} onChange={e => setPropertyFilter(e.target.value)}>
              <option value="all">Alle Objekte ({allTenants.length} Mieter)</option>
              {data.properties.map(p => {
                const n = allTenants.filter(t => t.propertyId === p.id).length;
                return <option key={p.id} value={p.id}>{p.name} ({n} Mieter)</option>;
              })}
            </select>
            <button className="msg-tmpl-btn" onClick={() => selectAll(!allInFilterSelected)}>
              {allInFilterSelected ? 'Keinen auswählen' : 'Alle in Filter auswählen'}
            </button>
          </div>
          <div className="bc-recipient-list">
            {filteredTenants.map(t => {
              const property = data.properties.find(p => p.id === t.propertyId);
              const unit = property?.units.find(u => u.id === t.unitId);
              return (
                <label key={t.id} className={`bc-recipient ${selected.has(t.id) ? 'selected' : ''}`}>
                  <input type="checkbox" checked={selected.has(t.id)} onChange={() => toggle(t.id)} />
                  <div className="bc-recipient-avatar">{t.name.split(' ').map(s => s[0]).slice(0, 2).join('')}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="bc-recipient-name">{t.name}</div>
                    <div className="bc-recipient-meta">{property?.name}{unit ? ` · ${unit.label}` : ''} · {t.email}</div>
                  </div>
                </label>
              );
            })}
            {filteredTenants.length === 0 && (
              <div className="empty" style={{ padding: 32, fontSize: 12, color: 'var(--ink-4)' }}>Keine Mieter im Filter.</div>
            )}
          </div>
          <div className="modal-foot">
            <button className="btn btn-ghost btn-sm" onClick={() => setStep(1)}>← Zurück</button>
            <button className="btn btn-primary btn-sm" disabled={selected.size === 0} onClick={() => setStep(3)}>
              Weiter — {selected.size} Empfänger →
            </button>
          </div>
        </div>
      </div>
    );
  }

  // Step 3: preview & send
  const sampleTenant = allTenants.find(t => selected.has(t.id));
  const sampleProperty = sampleTenant && data.properties.find(p => p.id === sampleTenant.propertyId);
  const sampleUnit = sampleProperty?.units.find(u => u.id === sampleTenant.unitId);
  const samplePreview = sampleTenant ? tpl.build({ tenant: sampleTenant, property: sampleProperty, unit: sampleUnit, fields: tmplFields }) : '';

  return (
    <div className="modal-backdrop" onClick={sending ? undefined : onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 720, maxHeight: '88vh' }}>
        <div className="modal-head">
          <div>
            <div className="modal-title">📢 Rundmail · {tpl.name}</div>
            <div className="modal-sub">Schritt 3 von 3 — Felder anpassen & Vorschau</div>
          </div>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>
        <div className="bc-step3">
          <div className="bc-fields">
            <div className="tmpl-fields-head" style={{ marginBottom: 8 }}>
              <span className="tmpl-fields-icon">{tpl.icon}</span>
              <span className="tmpl-fields-name">Felder</span>
            </div>
            <div className="tmpl-fields-grid">
              {tpl.fields.map(f => (
                <label key={f.id} className="tmpl-field">
                  <span className="tmpl-field-label">{f.label}</span>
                  <input
                    type={f.type === 'date' ? 'date' : 'text'}
                    className="tmpl-field-input"
                    value={tmplFields[f.id] ?? ''}
                    onChange={e => setTmplFields({ ...tmplFields, [f.id]: f.type === 'currency' ? Number(e.target.value) : e.target.value })}
                  />
                </label>
              ))}
            </div>
            <div className="bc-summary">
              <div className="bc-summary-row">
                <span>Empfänger</span>
                <strong>{selected.size}</strong>
              </div>
              <div className="bc-summary-row">
                <span>Kanal</span>
                <strong>E-Mail</strong>
              </div>
              <div className="bc-summary-row">
                <span>Betreff</span>
                <strong style={{ fontSize: 12, fontWeight: 500 }}>{tpl.subject}</strong>
              </div>
            </div>
          </div>
          <div className="bc-preview">
            <div className="bc-preview-head">
              Vorschau für <strong>{sampleTenant?.name}</strong>
              <span style={{ marginLeft: 'auto', fontSize: 10.5, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.08em' }}>1 von {selected.size}</span>
            </div>
            <div className="bc-preview-body">{samplePreview}</div>
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn btn-ghost btn-sm" onClick={() => setStep(2)} disabled={sending}>← Empfänger ändern</button>
          <button className="btn btn-primary btn-sm" onClick={doSend} disabled={sending}>
            {sending ? `Sende... ${sent}/${selected.size}` : `📨 An ${selected.size} Mieter senden`}
          </button>
        </div>
      </div>
    </div>
  );
}

// ───── Template picker modal ─────
function TemplateModal({ open, onClose, onPick }) {
  if (!open) return null;
  const cats = [...new Set(TEMPLATES.map(t => t.category))];
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 540 }}>
        <div className="modal-head">
          <div>
            <div className="modal-title">Vorlage einfügen</div>
            <div className="modal-sub">Felder werden automatisch mit Mieter- und Objektdaten gefüllt</div>
          </div>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>
        <div className="tmpl-list">
          {cats.map(cat => (
            <div key={cat} className="tmpl-cat">
              <div className="tmpl-cat-label">{cat}</div>
              {TEMPLATES.filter(t => t.category === cat).map(t => (
                <button key={t.id} className="tmpl-row" onClick={() => onPick(t)}>
                  <span className="tmpl-icon">{t.icon}</span>
                  <span className="tmpl-name">{t.name}</span>
                  <span className="tmpl-arrow">→</span>
                </button>
              ))}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ───── Template field editor ─────
function TemplateFieldEditor({ template, fields, setFields }) {
  return (
    <div className="tmpl-fields">
      <div className="tmpl-fields-head">
        <span className="tmpl-fields-icon">{template.icon}</span>
        <span className="tmpl-fields-name">{template.name}</span>
        <span className="tmpl-fields-sub">Felder anpassen — der Text passt sich automatisch an</span>
      </div>
      <div className="tmpl-fields-grid">
        {template.fields.map(f => (
          <label key={f.id} className="tmpl-field">
            <span className="tmpl-field-label">{f.label}</span>
            <input
              type={f.type === 'date' ? 'date' : 'text'}
              className="tmpl-field-input"
              value={fields[f.id] ?? ''}
              onChange={e => setFields({ ...fields, [f.id]: f.type === 'currency' ? Number(e.target.value) || 0 : e.target.value })}
              inputMode={f.type === 'currency' ? 'numeric' : undefined}
            />
          </label>
        ))}
      </div>
    </div>
  );
}

// ───── Owner avatar ─────
function OwnerAvatar({ ownerId, size = 22 }) {
  const o = OWNERS.find(x => x.id === ownerId);
  if (!o) return <div className="owner-av none" style={{ width: size, height: size }} title="Nicht zugewiesen">?</div>;
  return (
    <div className="owner-av" style={{ width: size, height: size, background: o.color, fontSize: size * 0.42 }} title={`${o.name} · ${o.role}`}>
      {o.initials}
    </div>
  );
}

// ───── Assign popover ─────
function AssignControl({ thread }) {
  const ctx = useMessages();
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);
  const owner = OWNERS.find(o => o.id === thread.assignedTo);
  return (
    <div className="assign-wrap" ref={ref}>
      <button className="assign-btn" onClick={() => setOpen(o => !o)}>
        <OwnerAvatar ownerId={thread.assignedTo} size={20} />
        <span style={{ fontSize: 12, color: 'var(--ink-2)' }}>
          {owner ? owner.name : 'Nicht zugewiesen'}
        </span>
        <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 4l3 3 3-3"/></svg>
      </button>
      {open && (
        <div className="assign-pop">
          <div className="assign-pop-label">Zuweisen an</div>
          {OWNERS.map(o => (
            <button key={o.id} className={`assign-pop-item ${o.id === thread.assignedTo ? 'active' : ''}`}
              onClick={() => { ctx.assignThread(thread.id, o.id); setOpen(false); }}>
              <OwnerAvatar ownerId={o.id} size={22} />
              <div>
                <div style={{ fontSize: 13 }}>{o.name}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>{o.role}</div>
              </div>
              {o.id === thread.assignedTo && <span style={{ marginLeft: 'auto' }}>✓</span>}
            </button>
          ))}
          <div className="assign-pop-divider"></div>
          <button className={`assign-pop-item ${!thread.assignedTo ? 'active' : ''}`}
            onClick={() => { ctx.assignThread(thread.id, null); setOpen(false); }}>
            <div className="owner-av none" style={{ width: 22, height: 22 }}>?</div>
            <div style={{ fontSize: 13 }}>Nicht zugewiesen</div>
          </button>
        </div>
      )}
    </div>
  );
}

// ───── Messages page ─────
function MessagesPage({ data, route, setRoute }) {
  const ctx = useMessages();
  const [activeId, setActiveId] = React.useState(route.threadId || ctx.threads[0]?.id);
  const [filter, setFilter] = React.useState('all');
  const [scope, setScope] = React.useState('all'); // all | mine | unassigned
  const [draft, setDraft] = React.useState('');
  const [tmplOpen, setTmplOpen] = React.useState(false);
  const [activeTemplate, setActiveTemplate] = React.useState(null);
  const [tmplFields, setTmplFields] = React.useState({});
  const [newOpen, setNewOpen] = React.useState(false);
  const [broadcastOpen, setBroadcastOpen] = React.useState(false);
  const newMenuRef = React.useRef(null);
  React.useEffect(() => {
    if (!newOpen) return;
    const h = (e) => { if (newMenuRef.current && !newMenuRef.current.contains(e.target)) setNewOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [newOpen]);

  // sync route → activeId
  React.useEffect(() => {
    if (route.threadId) setActiveId(route.threadId);
  }, [route.threadId]);

  // Handle pending compose from outside
  React.useEffect(() => {
    if (ctx.pendingCompose) {
      const pc = ctx.pendingCompose;
      if (pc.threadId) setActiveId(pc.threadId);
      if (pc.templateId) {
        const tpl = TEMPLATES.find(t => t.id === pc.templateId);
        if (tpl) {
          openTemplate(tpl, pc.ctx);
        }
      }
      ctx.setPendingCompose(null);
    }
  }, [ctx.pendingCompose]);

  // mark read on view
  React.useEffect(() => {
    if (activeId) {
      const t = ctx.threads.find(t => t.id === activeId);
      if (t && t.unread > 0) ctx.markRead(activeId);
    }
  }, [activeId]);

  const visible = ctx.threads.filter(t => {
    if (scope === 'mine' && t.assignedTo !== ME) return false;
    if (scope === 'unassigned' && t.assignedTo) return false;
    if (filter === 'unread') return t.unread > 0;
    if (filter === 'reply') return t.needsReply;
    return true;
  });

  const active = ctx.threads.find(t => t.id === activeId) || ctx.threads[0];
  const property = active && data.properties.find(p => p.id === active.propertyId);
  const unit = property?.units.find(u => u.id === active?.unitId);
  const tenant = active?.tenantId ? data.tenants[active.tenantId] : null;

  function openTemplate(tpl, extraCtx = {}) {
    setActiveTemplate(tpl);
    const ctxData = { tenant, unit, property, ...extraCtx };
    const initFields = {};
    tpl.fields.forEach(f => {
      const def = typeof f.default === 'function' ? f.default(ctxData) : f.default;
      initFields[f.id] = def;
    });
    setTmplFields(initFields);
    // Build initial body
    const body = tpl.build({ tenant, unit, property, fields: initFields, ...extraCtx });
    setDraft(body);
    setTmplOpen(false);
  }

  // Re-render body when fields change
  React.useEffect(() => {
    if (activeTemplate) {
      const body = activeTemplate.build({ tenant, unit, property, fields: tmplFields });
      setDraft(body);
    }
  }, [tmplFields]);

  function send() {
    if (!draft.trim() || !active) return;
    const now = new Date();
    const newMsg = {
      id: Date.now(),
      from: 'owner',
      senderId: ME,
      date: now.toISOString(),
      body: draft.trim(),
      receipt: { sent: now.toISOString(), delivered: null, read: null },
    };
    ctx.appendMessage(active.id, newMsg);
    setDraft('');
    setActiveTemplate(null);
    setTmplFields({});
    // simulate delivery + read
    setTimeout(() => {
      ctx.setThreads(prev => prev.map(t => t.id === active.id ? {
        ...t,
        messages: t.messages.map(m => m.id === newMsg.id ? { ...m, receipt: { ...m.receipt, delivered: new Date().toISOString() } } : m)
      } : t));
    }, 1500);
    setTimeout(() => {
      ctx.setThreads(prev => prev.map(t => t.id === active.id ? {
        ...t,
        messages: t.messages.map(m => m.id === newMsg.id ? { ...m, receipt: { ...m.receipt, read: new Date().toISOString() } } : m)
      } : t));
    }, 4500);
  }

  if (!active) return <div className="page"><div className="empty">Kein Thread.</div></div>;

  const counts = {
    all: ctx.threads.length,
    mine: ctx.threads.filter(t => t.assignedTo === ME).length,
    unassigned: ctx.threads.filter(t => !t.assignedTo).length,
  };

  // Empty state — no messages yet
  if (ctx.threads.length === 0) {
    return (
      <div className="page">
        <div className="page-header"><div><h1 className="page-title">Nachrichten</h1><div className="page-subtitle">Posteingang für Mieter-Kommunikation</div></div></div>
        <div className="empty" style={{ padding: '60px 20px', textAlign: 'center' }}>
          <div style={{ fontSize: 36, marginBottom: 8 }}>✉</div>
          <div className="empty-title">Noch keine Nachrichten</div>
          <div style={{ color: 'var(--ink-3)', maxWidth: 380, margin: '0 auto' }}>Sobald du E-Mail/SMS-Anbindung verbindest und Mieter anschreibst, erscheinen die Threads hier. Modul ist in Entwicklung.</div>
        </div>
      </div>
    );
  }

  return (
    <div className="page" style={{ paddingBottom: 0 }}>
      <div className="page-header">
        <div>
          <h1 className="page-title">Nachrichten</h1>
          <div className="page-subtitle">
            Geteilter Posteingang · {OWNERS.map(o => o.name).join(' · ')}
          </div>
        </div>
        <div className="page-actions">
          <div className="scope-tabs">
            <button className={`scope-tab ${scope === 'all' ? 'active' : ''}`} onClick={() => setScope('all')}>
              Alle <span className="scope-tab-c">{counts.all}</span>
            </button>
            <button className={`scope-tab ${scope === 'mine' ? 'active' : ''}`} onClick={() => setScope('mine')}>
              <OwnerAvatar ownerId={ME} size={16} /> Meine <span className="scope-tab-c">{counts.mine}</span>
            </button>
            <button className={`scope-tab ${scope === 'unassigned' ? 'active' : ''}`} onClick={() => setScope('unassigned')}>
              ? Offen <span className="scope-tab-c">{counts.unassigned}</span>
            </button>
          </div>
          <div ref={newMenuRef} style={{ position: 'relative' }}>
            <button className="btn btn-primary" onClick={() => setNewOpen(o => !o)}>＋ Neue Nachricht ▾</button>
            {newOpen && (
              <div className="new-msg-menu">
                <button className="new-msg-menu-item" onClick={() => { setNewOpen(false); }}>
                  <span className="new-msg-menu-icon">✉</span>
                  <div>
                    <div className="new-msg-menu-name">Einzelne Nachricht</div>
                    <div className="new-msg-menu-sub">An einen Mieter — aus der Mieterliste starten</div>
                  </div>
                </button>
                <button className="new-msg-menu-item" onClick={() => { setNewOpen(false); setBroadcastOpen(true); }}>
                  <span className="new-msg-menu-icon">📢</span>
                  <div>
                    <div className="new-msg-menu-name">Rundmail</div>
                    <div className="new-msg-menu-sub">An mehrere Mieter gleichzeitig — Wartung, Hausordnung, Müllkalender …</div>
                  </div>
                </button>
              </div>
            )}
          </div>
        </div>
      </div>

      <div className="msg-layout">
        <div className="msg-list-pane">
          <div className="msg-filter">
            {[
              { v: 'all', l: 'Alle' },
              { v: 'unread', l: 'Ungelesen' },
              { v: 'reply', l: 'Antwort offen' },
            ].map(f => (
              <button key={f.v} className={`msg-filter-btn ${filter === f.v ? 'active' : ''}`} onClick={() => setFilter(f.v)}>
                {f.l}
              </button>
            ))}
          </div>
          <div className="msg-list-scroll">
            {visible.length === 0 && <div className="empty" style={{ padding: 32, fontSize: 12, color: 'var(--ink-4)' }}>Keine Nachrichten in dieser Ansicht.</div>}
            {visible.map(t => {
              const last = t.messages[t.messages.length - 1];
              const prop = data.properties.find(p => p.id === t.propertyId);
              return (
                <button key={t.id} className={`msg-list-item ${activeId === t.id ? 'active' : ''}`}
                  onClick={() => setActiveId(t.id)}>
                  <div className="msg-list-avatar">{t.with.split(' ').map(s => s[0]).slice(0, 2).join('')}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="msg-list-row">
                      <span className="msg-list-name">{t.with}</span>
                      <span className="msg-list-when">{t.lastDate ? fmtTime(t.lastDate) : 'Entwurf'}</span>
                    </div>
                    <div className="msg-list-row">
                      <span className="msg-list-meta">
                        <span className={`msg-channel-dot ${t.channel}`}></span>
                        {t.role} · {prop?.name}
                      </span>
                      <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                        <OwnerAvatar ownerId={t.assignedTo} size={16} />
                        {t.unread > 0 && <span className="msg-unread-pill">{t.unread}</span>}
                      </span>
                    </div>
                    <div className="msg-list-preview">{last ? last.body : <em style={{ color: 'var(--ink-4)' }}>Neuer Entwurf — keine Nachrichten</em>}</div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        <div className="msg-thread-pane">
          <div className="msg-thread-head">
            <div>
              <div className="msg-thread-title">{active.with}</div>
              <div className="msg-thread-sub">
                {active.role} · {property?.name}{unit ? ` · ${unit.label}` : ''} · {active.channel === 'email' ? 'E-Mail' : 'Chat'}
              </div>
            </div>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <AssignControl thread={active} />
              <button className="btn btn-sm btn-ghost">Profil</button>
            </div>
          </div>

          <div className="msg-thread-body">
            {active.messages.length === 0 && (
              <div style={{ textAlign: 'center', color: 'var(--ink-4)', fontSize: 12, padding: 40 }}>
                Neuer Entwurf — verfassen Sie unten Ihre erste Nachricht
                {tenant && <div style={{ marginTop: 6, fontSize: 11.5 }}>📧 {tenant.email}</div>}
              </div>
            )}
            {active.messages.map(m => (
              <div key={m.id} className={`msg-bubble-row ${m.from}`}>
                <div className={`msg-bubble ${m.from}`}>
                  {m.from === 'owner' && m.senderId && m.senderId !== ME && (
                    <div className="msg-bubble-sender">
                      <OwnerAvatar ownerId={m.senderId} size={14} /> {OWNERS.find(o => o.id === m.senderId)?.name}
                    </div>
                  )}
                  <div className="msg-bubble-body">{m.body}</div>
                  {m.attachment && <div className="msg-bubble-att">📎 {m.attachment}</div>}
                  <div className="msg-bubble-foot">
                    <span className="msg-bubble-time">{fmtTime(m.date)}</span>
                    {m.from === 'owner' && <ReadReceipt receipt={m.receipt} channel={active.channel} />}
                  </div>
                </div>
              </div>
            ))}
          </div>

          <div className="msg-composer">
            {activeTemplate && (
              <TemplateFieldEditor template={activeTemplate} fields={tmplFields} setFields={setTmplFields} />
            )}
            <div className="msg-composer-bar">
              <button className="msg-tmpl-btn" onClick={() => setTmplOpen(true)}>📋 Vorlage</button>
              <button className="msg-tmpl-btn">📎 Anhang</button>
              {activeTemplate && (
                <button className="msg-tmpl-btn" onClick={() => { setActiveTemplate(null); setDraft(''); }}>Vorlage entfernen</button>
              )}
              <span style={{ marginLeft: 'auto', fontSize: 11, color: 'var(--ink-4)' }}>
                Versand via {active.channel === 'email' ? 'E-Mail (SMTP)' : 'Chat-API'}
              </span>
            </div>
            <textarea
              className="msg-composer-input"
              placeholder={`Nachricht an ${active.with}...`}
              value={draft}
              onChange={e => setDraft(e.target.value)}
              rows={activeTemplate ? 8 : 3}
            />
            <div className="msg-composer-foot">
              <span style={{ fontSize: 11.5, color: 'var(--ink-4)' }}>
                {tenant && `📧 ${tenant.email}`}
              </span>
              <button className="btn btn-primary btn-sm" onClick={send} disabled={!draft.trim()}>Senden ↗</button>
            </div>
          </div>
        </div>
      </div>

      <TemplateModal open={tmplOpen} onClose={() => setTmplOpen(false)} onPick={openTemplate} />
      <BroadcastModal open={broadcastOpen} onClose={() => setBroadcastOpen(false)} data={data} />
    </div>
  );
}

function fmtTime(iso) {
  const d = new Date(iso);
  const now = new Date(2026, 4, 1, 12, 0);
  const sameDay = d.toDateString() === now.toDateString();
  if (sameDay) return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
  const diff = (now - d) / (1000 * 60 * 60 * 24);
  if (diff < 7) return d.toLocaleDateString('de-DE', { weekday: 'short' });
  return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' });
}

Object.assign(window, { NotificationBell, MessagesPage, MessagesProvider, StartMessageButton, useMessages: () => React.useContext(MessagesContext), MessagesContext });
