from flask import Flask, jsonify, render_template_string
import subprocess
from pathlib import Path

app = Flask(__name__)

PROJECTS = [
    {'id': 'youtube', 'name': 'YouTube Research', 'desc': 'Video-Datenbank & KI-Analyse. Transkripte, Skills, Filter.', 'icon': '📊', 'url': 'https://youtube.agentsolutions-mallorca.com', 'status_type': 'systemd', 'service': 'youtube-research.service'},
    {'id': 'content-extraction', 'name': 'YouTube Extraction', 'desc': 'Inhalte extrahieren, bewerten, filtern und priorisieren. (geschuetzt, Basic Auth)', 'icon': '🗂️', 'url': 'https://content-extraction.agentsolutions-mallorca.com', 'status_type': 'systemd', 'service': 'content-extraction.service'},
    {'id': 'youtube-lm', 'name': 'YouTube LM', 'desc': 'Verifizierte Wissensaufbereitung, Lernstruktur und Audio-Skripte aus YouTube-Videos.', 'icon': '🎧', 'url': 'https://youtube.agentsolutions-mallorca.com/youtube-lm', 'status_type': 'systemd', 'service': 'youtube-research.service'},
    {'id': 'leads', 'name': 'Lead Engine', 'desc': 'B2B Lead Enrichment & Dashboard. Kampagnen-Verwaltung.', 'icon': '🎯', 'url': 'https://leads.agentsolutions-mallorca.com', 'status_type': 'docker', 'container': 'lead-engine'},
    {'id': 'telefon', 'name': 'Telefon Agent', 'desc': 'Twilio IVR + KI-Sprachassistent fuer eingehende Anrufe.', 'icon': '☎', 'url': 'https://telefon.agentsolutions-mallorca.com', 'status_type': 'static', 'status_value': 'in Entwicklung'},
    {'id': 'vento', 'name': 'Vento', 'desc': 'WhatsApp-Empfangsassistent. Erkennt Kunden, führt Erstgespräch, bereitet Rückruf vor.', 'icon': '💬', 'url': 'https://vento.agentsolutions-mallorca.com/docs', 'status_type': 'docker', 'container': 'vento', 'db_url': 'https://vento.agentsolutions-mallorca.com/customers'},
    {'id': 'solar', 'name': 'Solar-Wirtschaftlichkeits-App', 'desc': 'Photovoltaik, Eigenverbrauch, Förderung und Amortisation berechnen.', 'icon': '☀️', 'url': 'https://solar.agentsolutions-mallorca.com', 'vip_url': 'https://solar.agentsolutions-mallorca.com/admin/vip', 'status_type': 'systemd', 'service': 'solar-wirtschaftlichkeit.service'},
    {'id': 'sma-mas', 'name': 'Social Media Agent – MAS', 'desc': 'Mallorca Air Services / Lüftungsprofi. Radar, Content Queue und Publishing für Lüftung, Feuchte und Immobilien.', 'icon': '📡', 'url': 'https://sma-mas.agentsolutions-mallorca.com/cockpit?days=1&sort=op_priority&tab=radar', 'status_type': 'systemd', 'service': 'sma-core-api.service', 'group': 'sma'},
    {'id': 'sma-asol', 'name': 'Social Media Agent – ASO', 'desc': 'Agent Solutions. KI-Unternehmensberatung, individuelle Speziallösungen, Automatisierung und Agents.', 'icon': '🧠', 'url': 'https://sma-asol.agentsolutions-mallorca.com', 'status_type': 'static', 'status_value': 'preview', 'group': 'sma'},
    {'id': 'donna', 'name': 'Donna', 'desc': 'Lokale Windows-Assistentin (Electron). Aktueller Release: 1.1.2-free-models.', 'icon': '🗂️', 'url': 'https://agentsolutions-mallorca.com/downloads/donna/', 'location': 'local', 'status_type': 'static', 'status_value': 'lokal'}
]


CUSTOMER_PROJECTS = [
    {
        'id': 'sop-agent-system', 'name': 'SOP → Agent-System', 'icon': '🧭',
        'desc': 'Wiederholbare Abläufe in versionierte, kontrollierbare Agenten-Workflows überführen.',
        'metrics': {'Aufbau': '23–48 h', 'Umsetzung': 'schnell · Pilot vorhanden', 'Potenzial': '9,0/10', 'Konfidenz': 'hoch', 'Erlös': 'Setup + 150–500 €/Monat'},
        'fields': {
            'Projektbeschreibung': 'Plan: Aus einer geprüften SOP wird ein Agent-/Skill-Entwurf mit klaren Eingaben, Ausgaben, Eskalationen und Human-Handoff.',
            'Zielkunde': 'Plan: Unternehmen mit wiederkehrenden Kundenanfragen und dokumentierbaren Abläufen.',
            'Kundenproblem': 'Plan: Wissen ist verteilt, Abläufe sind personenabhängig und Übergaben nicht einheitlich.',
            'Anforderungsprofil': 'Plan: stabile SOP, freigegebene Datenquellen, Verantwortliche für Abnahme, Datenschutz- und Berechtigungskonzept.',
            'Technischer Aufbau': 'Plan: versionierte SOP → deterministische Validierung → Agent-/Skill-Ausgabe → Audit → Human-Review-Queue; produktive Kanaladapter nur nach Freigabe.',
            'Entwicklungsaufwand': 'Planwert: Discovery, Modellierung, Tests, Sicherheitsgrenzen und read-only Shadow-Run je Anwendungsfall; Aufwand wird nach Scope kalkuliert.',
            'Laufender Aufwand': 'Planwert: Pflege von SOP/Versionen, Testfällen, Monitoring und fachlicher Abnahme.',
            'Vertragsmodelle': 'Plan: einmalige Konzeption/Einrichtung plus laufende Wartung; optionale Weiterentwicklung nach Aufwand.',
            'Ertragspotenzial': 'Planwert: projekt- und betreuungsabhängig; keine Ist-Umsätze hinterlegt.',
            'Mögliche Kundenzahl / Marktpotenzial': 'Plan: branchenübergreifend für Prozesse mit wiederkehrenden Anfragen; belastbare Marktzahl noch zu erheben.',
            'Kundengewinnung / Akquisewege': 'Plan: bestehende Beratungskontakte, Prozess-Workshops, Referenz-Demos und zielgerichtete B2B-Ansprache.',
            'Risiken und Folgen': 'Fehlende SOP-Qualität, Datenschutz, falsche Klassifikation oder unklare Freigaben können zu Fehlübergaben führen; Human-Gate bleibt erforderlich.',
            'Cross-Selling-Möglichkeiten': 'Plan: Lead-Aufnahme, Prozessautomatisierung, Wissensaufbereitung und betreute Agenten-Workflows.',
            'Aktueller Status': 'Verifiziert: Pilotentwurf ASI-LEAD-INTAKE v1.0.0; lokaler Verifier PASS. Nicht produktiv integriert.',
            'Nächste Schritte': 'Read-only Shadow-Adapter mit synthetischen Fixtures, danach fachliche/Datenschutz-/Systemowner-Abnahme und erst dann begrenzter Kanalpilot.',
            'Vorhandene Demos / Referenzen / Agenten / Tools': 'Real vorhanden: /opt/struktur/agent-solutions-cockpit/pilot-lead-intake-v1 mit SOP, Spezifikation, 12 synthetischen Testfällen, verify_pilot.py und Verifikationsbericht. Keine produktiven Kanaladapter oder CRM-Schreibzugriffe.'
        }
    },
    {
        'id': 'prozess-human-loop', 'name': 'Prozessautomatisierung + Human-in-the-loop', 'icon': '🔁',
        'desc': 'Automatisierung mit definierten Prüf-, Freigabe- und Eskalationspunkten.',
        'metrics': {'Aufbau': '40–80 h', 'Umsetzung': 'mittel', 'Potenzial': '8,7/10', 'Konfidenz': 'hoch', 'Erlös': 'Projekt + 200–1.000 €/Monat'},
        'fields': {
            'Projektbeschreibung': 'Plan: Wiederkehrende Prozessschritte automatisieren, kritische Entscheidungen aber nachvollziehbar einem Menschen vorlegen.',
            'Zielkunde': 'Plan: Teams mit hohem manuellen Bearbeitungsanteil und klaren Freigabepflichten.',
            'Kundenproblem': 'Plan: Durchlaufzeiten und Fehler steigen, wenn Automatisierung ohne Kontrolle eingesetzt wird.',
            'Anforderungsprofil': 'Plan: Prozessdiagramm, Rollen/RBAC, Freigabekriterien, Datenminimierung und Audit-Anforderungen.',
            'Technischer Aufbau': 'Plan: Trigger → Agent/Workflow → Validierung → Human-Queue → freigegebene Folgeaktion → Audit.',
            'Entwicklungsaufwand': 'Planwert: Prozessaufnahme, Integrationen, Rollenmodell, Testfälle und Betriebskonzept; nach Schnittstellenumfang.',
            'Laufender Aufwand': 'Planwert: Monitoring, Regelpflege, Review-Queue und Anpassung an Prozessänderungen.',
            'Vertragsmodelle': 'Plan: Implementierungsprojekt, Retainer für Betrieb oder modulare Weiterentwicklung.',
            'Ertragspotenzial': 'Planwert: abhängig von Prozesskritikalität, Einsparung und Integrationsumfang; keine Ist-Werte hinterlegt.',
            'Mögliche Kundenzahl / Marktpotenzial': 'Plan: viele KMU-Prozesse geeignet; konkrete Segmentierung und Marktzahl offen.',
            'Kundengewinnung / Akquisewege': 'Plan: Prozessanalyse-Workshop, bestehende Automationsprojekte, Partner und Fallstudien.',
            'Risiken und Folgen': 'Zu breite Berechtigungen, fehlende Eskalationen oder schlechte Datenqualität können Fehlaktionen verursachen; Start read-only.',
            'Cross-Selling-Möglichkeiten': 'Plan: SOP→Agent, Lead-Agent, Skill-Library und Content-Service.',
            'Aktueller Status': 'Planphase; kein verifizierter produktiver Projektstatus hinterlegt.',
            'Nächste Schritte': 'Einen abgegrenzten Prozess auswählen, Risiko-/Tool-Matrix erstellen und synthetischen Shadow-Run definieren.',
            'Vorhandene Demos / Referenzen / Agenten / Tools': 'Keine projektspezifische Demo oder Kundenreferenz als real verifiziert hinterlegt.'
        }
    },
    {
        'id': 'ai-rezeptionist-lead-agent', 'name': 'AI-Rezeptionist / Lead-Agent', 'icon': '☎',
        'desc': 'Eingehende Anfragen strukturiert erfassen, vorsichtig klassifizieren und an Menschen übergeben.',
        'metrics': {'Aufbau': '30–60 h Standard', 'Umsetzung': 'mittel · Vorarbeit vorhanden', 'Potenzial': '8,6/10', 'Konfidenz': 'mittel-hoch', 'Erlös': 'Setup + 250–800 €/Monat'},
        'fields': {
            'Projektbeschreibung': 'Plan: Digitaler Erstkontakt für Telefon, Web oder Messaging mit sicherer Übergabe statt autonomer Zusagen.',
            'Zielkunde': 'Plan: Unternehmen mit vielen eingehenden Anfragen und verpassten oder uneinheitlich bearbeiteten Leads.',
            'Kundenproblem': 'Plan: Anfragen gehen verloren, Pflichtdaten fehlen und Mitarbeitende müssen wiederholt vorsortieren.',
            'Anforderungsprofil': 'Plan: freigegebene Kanäle, Antwortleitfäden, Eskalationsregeln, Human-Queue, Datenschutz und Monitoring.',
            'Technischer Aufbau': 'Plan: Kanaladapter → Intake/Intent → Pflichtfeld- und Sicherheitsprüfung → Handoff → menschliche Bearbeitung; keine Zusagen ohne Freigabe.',
            'Entwicklungsaufwand': 'Planwert: je Kanal und Integration; inklusive Testfälle, Audit und Abnahme.',
            'Laufender Aufwand': 'Planwert: Dialog-/Regelpflege, Queue-Review, Monitoring und Datenschutzprozesse.',
            'Vertragsmodelle': 'Plan: Einrichtung plus monatliche Betreuung oder nutzungs-/volumenabhängiges Modell nach Kalkulation.',
            'Ertragspotenzial': 'Planwert: abhängig von Kanalvolumen und Betreuungsumfang; keine Ist-Umsätze hinterlegt.',
            'Mögliche Kundenzahl / Marktpotenzial': 'Plan: serviceintensive KMU und lokale Dienstleister; konkrete Marktgröße offen.',
            'Kundengewinnung / Akquisewege': 'Plan: Demo mit synthetischen Anfragen, lokale B2B-Ansprache, Partner und Prozess-Workshops.',
            'Risiken und Folgen': 'Halluzinierte Zusagen, Datenschutzverstöße und falsche Eskalation; Human-Review und externe_action_taken=false im Pilot.',
            'Cross-Selling-Möglichkeiten': 'Plan: SOP-Modellierung, Prozessautomatisierung, CRM-Integration nach Freigabe und Content-/Landingpage-Service.',
            'Aktueller Status': 'Verifiziert: ASI-LEAD-INTAKE ist ein lokaler Draft-only-Pilot; keine produktive VENTO-Route, kein Versand, keine CRM-Schreibaktion.',
            'Nächste Schritte': 'Read-only Shadow-Adapter, Owner-Abnahmen für RBAC/Aufbewahrung/Monitoring, danach genau ein freigegebener Kanal.',
            'Vorhandene Demos / Referenzen / Agenten / Tools': 'Real vorhanden: Pilotartefakte unter /opt/struktur/agent-solutions-cockpit/pilot-lead-intake-v1; 12 synthetische Testfälle, lokaler Verifier PASS.'
        }
    },
    {
        'id': 'skill-library-orchestration', 'name': 'Skill-Library + Agenten-Orchestrierung', 'icon': '🧩',
        'desc': 'Wiederverwendbare Skills, Rollen und Routing-Regeln für mehrere Agenten systematisieren.',
        'metrics': {'Aufbau': '80–200 h', 'Umsetzung': 'aufwendig · parallel', 'Potenzial': '8,0/10', 'Konfidenz': 'mittel-hoch', 'Erlös': 'indirekter Umsatzhebel'},
        'fields': {
            'Projektbeschreibung': 'Plan: Skills versionieren, Zuständigkeiten trennen und Aufgaben kontrolliert an passende Agenten routen.',
            'Zielkunde': 'Plan: Organisationen mit mehreren Automationen, Agenten oder Wissensdomänen.',
            'Kundenproblem': 'Plan: Einzelintegrationen wachsen unübersichtlich; Wissen und Zuständigkeiten sind schwer wartbar.',
            'Anforderungsprofil': 'Plan: Skill-Verantwortliche, Namens-/Versionskonventionen, Routing-Regeln, Berechtigungen und Testkatalog.',
            'Technischer Aufbau': 'Plan: Skill-Registry → Routing/Orchestrierung → spezialisierte Agenten → Ergebnisprüfung → Audit/Feedback.',
            'Entwicklungsaufwand': 'Planwert: Architektur, Migration und Testabdeckung abhängig von Agenten- und Skill-Anzahl.',
            'Laufender Aufwand': 'Planwert: Versionierung, Qualitätsprüfung, Routing-Hygiene und Betriebsmonitoring.',
            'Vertragsmodelle': 'Plan: Architekturprojekt plus laufender Governance-/Betriebsretainer.',
            'Ertragspotenzial': 'Planwert: skalierbar über wiederverwendbare Module; keine Ist-Werte hinterlegt.',
            'Mögliche Kundenzahl / Marktpotenzial': 'Plan: vor allem wachsende Automations- und Wissensorganisationen; konkrete Zahl offen.',
            'Kundengewinnung / Akquisewege': 'Plan: Architektur-Audit, Orchestrierungsworkshop, technische Demos und Partner.',
            'Risiken und Folgen': 'Fehlrouting, unklare Ownership und zu viele Berechtigungen; Änderungen müssen versioniert und reversibel sein.',
            'Cross-Selling-Möglichkeiten': 'Plan: alle vier weiteren Projekte als modulare Skills/Workflows.',
            'Aktueller Status': 'Planphase; kein projektspezifischer Fertigstellungsnachweis hinterlegt.',
            'Nächste Schritte': 'Skill-/Routing-Inventar erstellen, Minimal-Registry definieren und read-only Testlauf mit synthetischen Aufgaben.',
            'Vorhandene Demos / Referenzen / Agenten / Tools': 'Keine projektspezifische Kundenreferenz oder fertige Demo als real verifiziert hinterlegt.'
        }
    },
    {
        'id': 'content-landingpage-service', 'name': 'Content-/Landingpage-Service', 'icon': '✍',
        'desc': 'Recherche, Struktur und Erstellung von Content und Landingpages mit kontrollierter Freigabe.',
        'metrics': {'Aufbau': '20–50 h', 'Umsetzung': 'schnell', 'Potenzial': '7,7/10', 'Konfidenz': 'mittel', 'Erlös': '300–1.000 €/Monat'},
        'fields': {
            'Projektbeschreibung': 'Plan: Briefing, Recherche, Content-Produktion und Landingpage-Struktur als wiederholbaren Service bündeln.',
            'Zielkunde': 'Plan: KMU und Agenturen mit regelmäßigem Bedarf an qualifiziertem Content und Conversion-Seiten.',
            'Kundenproblem': 'Plan: Inhalte entstehen langsam, uneinheitlich oder ohne nachvollziehbaren Recherche- und Freigabeprozess.',
            'Anforderungsprofil': 'Plan: Markenleitfaden, Zielgruppe, Quellen-/Faktenprozess, CMS-Zugang nur mit Freigabe und klare Abnahme.',
            'Technischer Aufbau': 'Plan: Briefing → Recherche/Quellen → Entwurf → Qualitäts-/Faktencheck → Human-Freigabe → Veröffentlichung.',
            'Entwicklungsaufwand': 'Planwert: Templates, Content-Workflow und optionale CMS-Anbindung nach Scope.',
            'Laufender Aufwand': 'Planwert: Redaktionsplanung, Reviews, Aktualisierungen und Performance-Auswertung.',
            'Vertragsmodelle': 'Plan: Einzelprojekt, Content-Kontingent oder laufender Redaktionsretainer.',
            'Ertragspotenzial': 'Planwert: abhängig von Output, Kanal und Betreuung; keine Ist-Werte hinterlegt.',
            'Mögliche Kundenzahl / Marktpotenzial': 'Plan: branchenübergreifender Bedarf; belastbare Marktgröße noch zu validieren.',
            'Kundengewinnung / Akquisewege': 'Plan: Landingpage-Audit, Content-Workshop, bestehende Kontakte, Partner und eigene Demos.',
            'Risiken und Folgen': 'Falsche Fakten, Markenabweichungen, Urheberrechts- und Freigaberisiken; Veröffentlichung erst nach Human-Abnahme.',
            'Cross-Selling-Möglichkeiten': 'Plan: Wissenspipeline, SOP→Agent, Lead-Agent und Prozessautomatisierung.',
            'Aktueller Status': 'Planphase; kein projektspezifischer Fertigstellungsnachweis hinterlegt.',
            'Nächste Schritte': 'Standardbriefing, Freigabegates und ein synthetisches Beispielprojekt als interne Demo definieren.',
            'Vorhandene Demos / Referenzen / Agenten / Tools': 'Keine projektspezifische Demo oder Kundenreferenz als real verifiziert hinterlegt.'
        }
    },

    {
        'id': 'ki-wissens-onboarding', 'name': 'KI-Wissens- & Onboarding-System', 'icon': '◈',
        'desc': 'Unternehmenswissen sichern, quellenbasiert zugänglich machen und Einarbeitung beschleunigen.',
        'metrics': {'Aufbau': '30–70 h', 'Umsetzung': 'mittel · Infrastruktur vorhanden', 'Potenzial': '9,1/10', 'Konfidenz': 'hoch', 'Erlös': 'Setup + 200–700 €/Monat'},
        'fields': {}
    },

    {
        'id': 'procurement-savings-audit', 'name': 'Beschaffungs-Audit / Savings Finder', 'icon': '◎',
        'desc': 'Einstiegsangebot: Einkaufs- und Bestandsdaten prüfen, wirtschaftliche Hebel sichtbar machen und das passende Folgeprojekt ableiten.',
        'metrics': {'Aufbau': '8–20 h', 'Umsetzung': 'sehr schnell · Einstiegsangebot', 'Potenzial': '8,9/10', 'Konfidenz': 'mittel-hoch', 'Erlös': 'Audit 500–1.500 €'}, 'fields': {}
    },
    {
        'id': 'inventory-replenishment-agent', 'name': 'Bestands- & Nachbestell-Agent', 'icon': '▣',
        'desc': 'Bestand und Verbrauch überwachen und Nachbestellungen kontrolliert vorbereiten.',
        'metrics': {'Aufbau': '35–70 h', 'Umsetzung': 'mittel · gute Standardisierung', 'Potenzial': '8,8/10', 'Konfidenz': 'hoch', 'Erlös': 'Setup + 250–900 €/Monat'}, 'fields': {}
    },
    {
        'id': 'supplier-risk-monitoring-agent', 'name': 'Lieferanten-Risiko-Agent', 'icon': '⚠',
        'desc': 'Lieferanten auf Ausfall-, Preis-, Leistungs- und externe Risikosignale überwachen.',
        'metrics': {'Aufbau': '40–80 h', 'Umsetzung': 'mittel', 'Potenzial': '8,7/10', 'Konfidenz': 'hoch', 'Erlös': 'Setup + 300–1.000 €/Monat'}, 'fields': {}
    },
    {
        'id': 'spend-cost-analysis-agent', 'name': 'Spend- & Kostenanalyse-Agent', 'icon': 'Σ',
        'desc': 'Einkaufsdaten analysieren und Preisabweichungen sowie Einsparpotenziale sichtbar machen.',
        'metrics': {'Aufbau': '25–50 h', 'Umsetzung': 'schnell–mittel', 'Potenzial': '8,7/10', 'Konfidenz': 'mittel-hoch', 'Erlös': 'Analyse + 150–600 €/Monat'}, 'fields': {}
    },
    {
        'id': 'rfq-offer-comparison-agent', 'name': 'RFQ- & Angebotsvergleich-Agent', 'icon': '≋',
        'desc': 'Lieferantenanfragen und Angebote strukturiert vergleichen und Entscheidungsvorlagen erzeugen.',
        'metrics': {'Aufbau': '30–60 h', 'Umsetzung': 'mittel', 'Potenzial': '8,7/10', 'Konfidenz': 'mittel-hoch', 'Erlös': 'Setup + 200–700 €/Monat'}, 'fields': {}
    },

]


PRIORITY_ORDER = ['ki-wissens-onboarding','sop-agent-system','prozess-human-loop','ai-rezeptionist-lead-agent','skill-library-orchestration','content-landingpage-service']
CUSTOMER_PROJECTS.sort(key=lambda p: PRIORITY_ORDER.index(p['id']) if p['id'] in PRIORITY_ORDER else 999)

CUSTOMER_PROJECT_DOSSIERS = {
    'sop-agent-system': 'SOP-Agent-System.md',
    'prozess-human-loop': 'Prozessautomatisierung-Human-in-the-loop.md',
    'ai-rezeptionist-lead-agent': 'AI-Rezeptionist-Lead-Agent.md',
    'skill-library-orchestration': 'Skill-Library-Agenten-Orchestrierung.md',
    'content-landingpage-service': 'Content-Landingpage-Service.md',
    'ki-wissens-onboarding': 'KI-Wissens-Onboarding-System.md',
    'procurement-savings-audit': 'Beschaffungs-Audit-Savings-Finder.md',
    'inventory-replenishment-agent': 'Bestands-Nachbestell-Agent.md',
    'supplier-risk-monitoring-agent': 'Lieferanten-Risiko-Agent.md',
    'spend-cost-analysis-agent': 'Spend-Kostenanalyse-Agent.md',
    'rfq-offer-comparison-agent': 'RFQ-Angebotsvergleich-Agent.md',
}
CUSTOMER_PROJECT_VAULT_DIR = Path('/opt/obsidian-vault/Agent-Solutions/Kundenprojekte')

def load_customer_project_fields(project_id):
    filename = CUSTOMER_PROJECT_DOSSIERS.get(project_id)
    if not filename: return {}, None
    path = CUSTOMER_PROJECT_VAULT_DIR / filename
    if not path.exists(): return {}, str(path)
    text = path.read_text(encoding='utf-8')
    fields, current, buf = {}, None, []
    for line in text.splitlines():
        if line.startswith('## '):
            if current: fields[current] = '\n'.join(buf).strip()
            current, buf = line[3:].strip(), []
        elif current is not None:
            buf.append(line)
    if current: fields[current] = '\n'.join(buf).strip()
    return fields, str(path)

# Suchrichtungen sind eigenstaendige Gruppierungen; die Projekt-IDs referenzieren
# die zentrale CUSTOMER_PROJECTS-Liste, damit ein Projekt bei mehreren Richtungen
# nicht dupliziert werden muss.
SEARCH_DIRECTIONS = [
    {
        'id': 'business',
        'title': 'Suchrichtung: Business',
        'keywords': 'Business',
        'purpose': 'Recherche nach konkreten, verkaufbaren KI- und Automatisierungsleistungen fuer kleine und mittlere Unternehmen. Ziel ist es, wiederkehrende Kundenprobleme, umsetzbare Agenten-/Workflow-Ideen sowie standardisierbare Angebote mit realistischem Ertragspotenzial zu identifizieren.',
        'sort': 'Neueste importierte zuerst',
        'evaluated': '20 Videos ausgewertet',
        'usable': '19 verwertbar',
        'missing': '1 Transkript fehlt',
        'batch': '2 Batches · 2026-09-08',
        'recency': 'KI-Aktualität: max. 90 Tage · Fallback bis 120 Tage',
        'project_ids': ['ki-wissens-onboarding','sop-agent-system','prozess-human-loop','ai-rezeptionist-lead-agent','skill-library-orchestration','content-landingpage-service'],
    },
    {
        'id': 'einkauf-bestand',
        'title': 'Suchrichtung: Einkauf & Bestand',
        'keywords': 'Einkauf · Beschaffung · Bestand · Lieferanten · Sourcing',
        'purpose': 'Recherche nach klar abgrenzbaren KI- und Automatisierungsleistungen rund um Einkauf, Lieferanten, Kostenanalyse und Bestandssteuerung. Überschneidungen mit den allgemeinen Business-Projekten werden vermieden.',
        'sort': 'Neueste Veröffentlichung zuerst',
        'evaluated': '1 direkter YouTube-Treffer im aktuellen Bestand',
        'usable': '1 verwertbar · externe Validierung stark',
        'missing': 'YouTube-Abdeckung derzeit gering',
        'batch': '1 Batch · 2026-09-08',
        'recency': 'YouTube: max. 90 Tage · externe Quellen: Schwerpunkt 2026',
        'project_ids': ['procurement-savings-audit','inventory-replenishment-agent','supplier-risk-monitoring-agent','spend-cost-analysis-agent','rfq-offer-comparison-agent'],
    },
]

def get_status(project):
    status_type = project.get('status_type')

    if status_type == 'static':
        return project.get('status_value', 'pending')

    if status_type == 'docker':
        container = project.get('container')
        try:
            result = subprocess.run(
                ['docker', 'inspect', '--format', '{{.State.Status}}', container],
                capture_output=True,
                text=True,
                timeout=5
            )
            state = result.stdout.strip()
            return 'online' if state == 'running' else 'offline'
        except Exception:
            return 'offline'

    if status_type == 'systemd':
        service = project.get('service')
        try:
            result = subprocess.run(
                ['systemctl', 'is-active', service],
                capture_output=True,
                text=True,
                timeout=5
            )
            return 'online' if result.stdout.strip() == 'active' else 'offline'
        except Exception:
            return 'offline'

    return 'pending'

HTML = """<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Projekte</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0f1117; color: #e2e8f0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; min-height: 100vh; }
header { padding: 2rem 3rem; border-bottom: 1px solid #1e2330; display: flex; justify-content: space-between; align-items: center; }
header h1 { font-size: 1.5rem; font-weight: 600; color: #fff; }
header span { font-size: 0.85rem; color: #fff; }
header a { font-size: 0.8rem; color: #3b82f6; text-decoration: none; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem; padding: 3rem; }
.card { background: #161b27; border: 1px solid #1e2330; border-radius: 12px; padding: 1.5rem; }
.card-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 1rem; }
.card-icon { font-size: 2rem; }
.card-icon svg { display: block; width: 2.2rem; height: 2.2rem; }
.badge { font-size: 0.7rem; padding: 0.25rem 0.7rem; border-radius: 99px; font-weight: 600; }
.badge.online { background: #1a3a2a; color: #4ade80; border: 1px solid #166534; }
.badge.offline { background: #3a1a1a; color: #f87171; border: 1px solid #7f1d1d; }
.badge.pending { background: #2a2a2a; color: #fff; border: 1px solid #334155; }
.badge.lokal { background: #1e2a3a; color: #818cf8; border: 1px solid #3730a3; }
.badge.preview { background: #2a2440; color: #c4b5fd; border: 1px solid #6d28d9; }
.section-title { font-size: 0.85rem; font-weight: 700; color: #fff; text-transform: uppercase; letter-spacing: 1.5px; padding: 0 3rem; margin-top: 1.5rem; }
.card-title { font-size: 1.1rem; font-weight: 600; color: #fff; margin-bottom: 0.4rem; }
.card-desc { font-size: 0.85rem; color: #fff; line-height: 1.5; margin-bottom: 1.2rem; }
.card-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.btn { font-size: 0.75rem; padding: 0.4rem 0.9rem; border-radius: 6px; border: none; cursor: pointer; font-weight: 500; }
.btn-open { background: #1e3a5f; color: #60a5fa; }
.btn-restart { background: #1e3a2a; color: #4ade80; }
.btn-stop { background: #3a1e1e; color: #f87171; }
.btn-start { background: #1a3a2a; color: #4ade80; }
footer { text-align: center; padding: 2rem; color: #334155; font-size: 0.8rem; }
</style></head><body>
{% macro project_icon(p) -%}
{% if p.id == 'youtube' %}<svg viewBox="0 0 48 48" role="img" aria-label="YouTube Research" data-icon="youtube-research"><rect x="2" y="8" width="29" height="22" rx="6" fill="#ff0033"/><path d="M13 13l10 6-10 6z" fill="#fff"/><circle cx="35" cy="29" r="7" fill="none" stroke="#e2e8f0" stroke-width="3"/><path d="M40 34l5 5" stroke="#e2e8f0" stroke-width="3" stroke-linecap="round"/></svg>
{% elif p.id == 'youtube-lm' %}<svg viewBox="0 0 48 48" role="img" aria-label="YouTube LM" data-icon="youtube-lm"><rect x="2" y="8" width="29" height="22" rx="6" fill="#ff0033"/><path d="M13 13l10 6-10 6z" fill="#fff"/><path d="M34 18v12M39 14v20M44 19v10" stroke="#e2e8f0" stroke-width="3" stroke-linecap="round"/></svg>
{% elif p.id == 'content-extraction' %}<svg viewBox="0 0 48 48" role="img" aria-label="YouTube Extraction" data-icon="youtube-extraction"><rect x="2" y="8" width="29" height="22" rx="6" fill="#ff0033"/><path d="M13 13l10 6-10 6z" fill="#fff"/><path d="M35 13h7l4 4v19a3 3 0 0 1-3 3H35a3 3 0 0 1-3-3V16a3 3 0 0 1 3-3z" fill="none" stroke="#e2e8f0" stroke-width="2.5"/><path d="M42 13v5h4M36 24h7M36 29h7" stroke="#e2e8f0" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
{% else %}{{ p.icon }}{% endif %}
{%- endmacro %}
<header><div><h1>Projekte</h1><span>Projekte VPS</span></div>
<a href="https://agentsolutions-mallorca.com" style="font-size:0.85rem;color:#3b82f6;text-decoration:none;border:1px solid #1e3a5f;padding:0.4rem 1rem;border-radius:8px;">&#8592; Agent Solutions</a></header>
<div class="section-title">Social Media Agents</div>
<div class="grid" id="grid-sma">
{% for p in projects %}{% if p.group == 'sma' %}
<div class="card" id="card-{{p.id}}">
<div class="card-header"><div class="card-icon">{{ project_icon(p) }}</div>
<span class="badge pending" id="badge-{{p.id}}">...</span></div>
<div class="card-title">{{p.name}}</div>
<div class="card-desc">{{p.desc}}</div>
<div class="card-actions">
{% if p.url %}<button class="btn btn-open" onclick='openProjectWindow({{ p.url|tojson }})'>&#214;ffnen</button>{% endif %}
</div></div>
{% endif %}{% endfor %}
</div>
<div class="section-title">Weitere VPS-Projekte</div>
<div class="grid" id="grid">
{% for p in projects %}{% if p.location != 'local' and p.group != 'sma' %}
<div class="card" id="card-{{p.id}}">
<div class="card-header"><div class="card-icon">{{ project_icon(p) }}</div>
<span class="badge pending" id="badge-{{p.id}}">...</span></div>
<div class="card-title">{{p.name}}</div>
<div class="card-desc">{{p.desc}}</div>
<div class="card-actions">
{% if p.url %}<button class="btn btn-open" onclick='openProjectWindow({{ p.url|tojson }})'>&#214;ffnen</button>{% endif %}{% if p.vip_url %}<button class="btn" style="background:#2a2440;color:#c4b5fd" onclick='openProjectWindow({{ p.vip_url|tojson }})'>VIP-Zugänge</button>{% endif %}{% if p.db_url %}<button class="btn" style="background:#1e2a3a;color:#818cf8" onclick='openProjectWindow({{ p.db_url|tojson }})'>Kunden DB</button>{% endif %}
</div></div>
{% endif %}{% endfor %}
</div>
<div class="section-title">Lokale Projekte</div>
<div class="grid" id="grid-local">
{% for p in projects %}{% if p.location == 'local' %}
<div class="card" id="card-{{p.id}}">
<div class="card-header"><div class="card-icon">{{ project_icon(p) }}</div>
<span class="badge lokal" id="badge-{{p.id}}">lokal</span></div>
<div class="card-title">{{p.name}}</div>
<div class="card-desc">{{p.desc}}</div>
<div class="card-actions">
{% if p.url %}<a href="{{p.url}}" target="_blank"><button class="btn btn-open">&#214;ffnen</button></a>{% endif %}
</div></div>
{% endif %}{% endfor %}
</div>
<footer>Agent Solutions · Projekte · VPS IONOS 31.70.68.228</footer>
<script>
function openProjectWindow(url) {
  window.open(
    url,
    "_blank",
    "width=1400,height=900,resizable=yes,scrollbars=yes,noopener"
  );
}

function updateStatus() {
  fetch("/api/status").then(r=>r.json()).then(data => {
    data.forEach(s => {
      const b = document.getElementById("badge-"+s.id);
      b.textContent = s.status;
      const cls =
        s.status === "online" ? "online" :
        s.status === "offline" ? "offline" :
        s.status === "lokal" ? "lokal" :
        s.status === "preview" ? "preview" :
        "pending";
      b.className = "badge " + cls;
    });
  });
}
updateStatus();
setInterval(updateStatus, 15000);
</script></body></html>"""

@app.route('/')
def index():
    return render_template_string(HTML, projects=PROJECTS)

@app.route('/api/status')
def status():
    return jsonify([{'id': p['id'], 'status': get_status(p)} for p in PROJECTS])


CUSTOMER_PROJECTS_INDEX_HTML = """<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Kundenprojekte</title><style>
*{box-sizing:border-box}body{margin:0;background:#0f1117;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}header{padding:2rem 3rem;border-bottom:1px solid #1e2330;display:flex;justify-content:space-between;align-items:center}h1{margin:0;color:#fff;font-size:1.5rem}header span{color:#fff;font-size:.85rem}.back{color:#60a5fa;text-decoration:none;border:1px solid #1e3a5f;padding:.4rem 1rem;border-radius:8px}.wrap{padding:2.5rem 3rem;max-width:1400px;margin:auto}.intro{color:#fff;line-height:1.6;margin:0 0 1.5rem}.direction{margin:0 0 3rem;border:1px solid #263044;border-radius:14px;padding:1.25rem;background:#111621}.direction h2{color:#fff;font-size:1.45rem;margin:0 0 .9rem;padding:.85rem 1rem;background:#1a2231;border-left:5px solid #3b82f6;border-radius:8px;letter-spacing:.01em}.direction-purpose{max-width:1050px;color:#fff;font-size:.84rem;line-height:1.55;margin:-.2rem 0 .85rem;padding:.75rem .9rem;background:#121722;border-left:3px solid #3b82f6;border-radius:6px}.direction-meta{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1rem}.direction-meta span{background:#161b27;border:1px solid #263044;border-radius:7px;color:#fff;font-size:.78rem;padding:.45rem .65rem}.table-head{display:grid;grid-template-columns:minmax(240px,1.45fr) minmax(190px,1.05fr) repeat(5,minmax(100px,.65fr)) auto;gap:.8rem;padding:0 .9rem .45rem;color:#fff;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.grid{display:flex;flex-direction:column;gap:.85rem}.card{background:#161b27;border:1px solid #1e2330;border-radius:12px;padding:1rem 1.15rem;display:grid;grid-template-columns:minmax(240px,1.45fr) minmax(190px,1.05fr) repeat(5,minmax(100px,.65fr)) auto;gap:.8rem;align-items:center}.segments{font-size:.76rem;line-height:1.45;color:#fff}.segment-value{display:block;color:#fff}.project-main{display:grid;grid-template-columns:auto 1fr;gap:.8rem;align-items:center}.icon{font-size:1.8rem}.title{font-size:1rem;font-weight:600;color:#fff;margin-bottom:.2rem}.desc{font-size:.78rem;color:#fff;line-height:1.4}.metrics{display:contents}.metric{background:#0f1420;border:1px solid #263044;border-radius:8px;padding:.55rem}.metric-label{display:block;color:#fff;font-size:.64rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.18rem}.metric-value{display:block;color:#fff;font-size:.78rem;font-weight:650;line-height:1.25}.plan-note{display:none}.btn{display:inline-block;margin:0;background:#1e3a5f;color:#60a5fa;border:0;border-radius:6px;padding:.45rem .9rem;text-decoration:none;font-size:.8rem;font-weight:600;white-space:nowrap}footer{text-align:center;padding:2rem;color:#fff;font-size:.8rem}@media(max-width:1000px){.table-head{display:none}.card{grid-template-columns:1fr 1fr}.project-main,.segments,.btn{grid-column:1/-1}}@media(max-width:700px){header,.wrap{padding-left:1.25rem;padding-right:1.25rem}.card{grid-template-columns:1fr}.metric{min-height:55px}}
</style></head><body><header><div><h1>Kundenprojekte</h1><span>Projekte · Lösungsangebote und Entwicklungsstände</span></div><a class="back" href="/">← Projekte</a></header><main class="wrap"><p class="intro">Übersicht der ausgearbeiteten Projektangebote. Planwerte sind ausdrücklich als Plan gekennzeichnet; Ist-Zustände werden nur bei verifizierten Artefakten ausgewiesen.</p>{% for direction in search_directions %}<section class="direction" id="search-direction-{{direction.id}}"><h2>{{direction.title}} · {{direction.project_ids|length}} Einträge</h2><div class="direction-purpose"><strong>Recherche-Zweck:</strong> {{direction.purpose}}</div><div class="direction-meta"><span>Suchbegriffe: {{direction.keywords}}</span><span>Sortierung: {{direction.sort}}</span><span>{{direction.evaluated}}</span><span>{{direction.usable}}</span><span>{{direction.missing}}</span><span>Batch: {{direction.batch}}</span><span>{{direction.recency}}</span></div><div class="table-head"><span>Projekt</span><span>Interessant für</span><span>Aufbau</span><span>Umsetzung</span><span>Potenzial</span><span>Konfidenz</span><span>Erlös</span><span>Details</span></div><div class="grid">{% for p in customer_projects %}{% if p.id in direction.project_ids %}<article class="card" id="project-card-{{p.id}}"><div class="project-main"><div class="icon">{{p.icon}}</div><div><div class="title">{{p.name}}</div><div class="desc">{{p.desc}}</div></div></div><div class="segments"><span class="segment-value">{{p.segments}}</span></div><div class="metrics">{% for label,value in p.metrics.items() %}<div class="metric"><span class="metric-label">{{label}}</span><span class="metric-value">{{value}}</span></div>{% endfor %}</div><div class="plan-note">Planwerte zur internen Bewertung</div><a class="btn" href="/kundenprojekte/{{p.id}}">Details</a></article>{% endif %}{% endfor %}</div></section>{% endfor %}</main><footer>Agent Solutions · Projekte</footer></body></html>"""

CUSTOMER_PROJECT_DETAIL_HTML = """<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{project.name}} · Kundenprojekte</title><style>
*{box-sizing:border-box}body{margin:0;background:#0f1117;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}header{padding:2rem 3rem;border-bottom:1px solid #1e2330;display:flex;justify-content:space-between;align-items:center;gap:1rem}h1{margin:0;color:#fff;font-size:1.45rem}.sub{color:#fff;font-size:.85rem;margin-top:.4rem}.back{color:#60a5fa;text-decoration:none;border:1px solid #1e3a5f;padding:.4rem 1rem;border-radius:8px}.wrap{padding:2.5rem 3rem;max-width:1050px;margin:auto}.hero{background:#161b27;border:1px solid #1e2330;border-radius:12px;padding:1.5rem;margin-bottom:1.25rem}.hero-icon{font-size:2.3rem}.hero p{color:#fff;line-height:1.6}.note{color:#fbbf24;font-size:.82rem}.details{display:grid;gap:.65rem}details{background:#161b27;border:1px solid #1e2330;border-radius:9px;padding:0 1rem}summary{cursor:pointer;padding:1rem 0;color:#fff;font-weight:600}details p{color:#fff;line-height:1.6;font-size:.9rem;margin:0 0 1rem;white-space:pre-line}footer{text-align:center;padding:2rem;color:#fff;font-size:.8rem}@media(max-width:700px){header,.wrap{padding-left:1.25rem;padding-right:1.25rem;align-items:flex-start}}
</style></head><body><header><div><h1>{{project.icon}} {{project.name}}</h1><div class="sub">Kundenprojekte · Detailansicht</div></div><a class="back" href="/kundenprojekte">← Kundenprojekte</a></header><main class="wrap"><section class="hero"><p>{{project.desc}}</p>{% if project.dossier_path %}<p style="font-size:.8rem;color:#fff"><strong>Wissensquelle:</strong> {{project.dossier_path}} · für Obsidian/Graphiti</p>{% endif %}<div class="note">Planwerte und offene Punkte sind gekennzeichnet. Keine nicht verifizierten Fertigstellungen oder Kundenreferenzen.</div></section><section class="details">{% for label,value in project.fields.items() %}<details{% if loop.first %} open{% endif %}><summary>{{label}}</summary><p>{{value}}</p></details>{% endfor %}</section></main><footer>Agent Solutions · Projekte</footer></body></html>"""

@app.route('/kundenprojekte')
def kundenprojekte():
    projects=[]
    for source in CUSTOMER_PROJECTS:
        project=dict(source)
        fields,_=load_customer_project_fields(project['id'])
        project['segments']=fields.get('Interessant für / Zielbranchen','')
        projects.append(project)
    return render_template_string(
        CUSTOMER_PROJECTS_INDEX_HTML,
        customer_projects=projects,
        search_directions=SEARCH_DIRECTIONS,
    )

@app.route('/kundenprojekte/<project_id>')
def kundenprojekt_detail(project_id):
    project = next((item for item in CUSTOMER_PROJECTS if item['id'] == project_id), None)
    if project is None:
        return 'Projekt nicht gefunden', 404
    project = dict(project)
    dossier_fields, dossier_path = load_customer_project_fields(project_id)
    if dossier_fields:
        project['fields'] = dossier_fields
    project['dossier_path'] = dossier_path
    return render_template_string(CUSTOMER_PROJECT_DETAIL_HTML, project=project)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5001, debug=False)

