#define _CRT_SECURE_NO_WARNINGS
#pragma execution_character_set("utf-8")

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <locale>
#include <curl/curl.h>
#include <windows.h>
#include <commdlg.h>
#include <uxtheme.h>
#include <dwmapi.h>
#pragma comment(lib, "uxtheme.lib")
#pragma comment(lib, "dwmapi.lib")

#pragma comment(lib, "C:\\vcpkg\\installed\\x64-windows\\lib\\libcurl.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "normaliz.lib")

using namespace std;

struct These {
    string text;
    string antwort;
    string begruendung;
};

// Globale Variablen
HWND hChatDisplay;
HWND hTheseInput;
HWND hSendButton;
HWND hExportButton;
HFONT hFont;
HFONT hFontBold;
HBRUSH hBackgroundBrush;
HBRUSH hChatBrush;
HBRUSH hInputBrush;

string apiKey = "YOUR OPEN AI KEY";

vector<These> alleThesen;
string wahlKontext = "";
bool wahlFestgelegt = false;
bool batchLaeuft = false;

HWND hWahlPanel;
HWND hWahlInput;
HWND hWahlButton;
HWND hWahlLabel;
HWND hDateiButton;   // "Datei laden" Button
HWND hBatchButton;   // "Alle analysieren" Button
HWND hResetButton;   // "Zurück" Button
HWND hWahlKarten[5]; // Anklickbare Wahl-Karten
HWND hHintLabel;     // "Oder manuell eingeben:" Label
HBRUSH hScrollBrush; // Dunkler Scrollbalken

// Vollständige Wahlkontexte für die 5 Karten
const char* wahlProfile[5] = {
    "Landtagswahl Baden-W\xC3\xBCrttemberg (8. M\xC3\xA4rz 2026)",
    "Landtagswahl Rheinland-Pfalz (22. M\xC3\xA4rz 2026)",
    "Landtagswahl Sachsen-Anhalt (6. September 2026)",
    "Abgeordnetenhauswahl Berlin (20. September 2026)",
    "Landtagswahl Mecklenburg-Vorpommern (20. September 2026)"
};

// Kartenfarben pro Wahl (Akzentfarben)
const COLORREF wahlFarben[5] = {
    RGB(0, 100, 60),    // BW: grün
    RGB(180, 0, 0),     // RLP: rot
    RGB(0, 80, 160),    // SA: blau
    RGB(180, 30, 0),    // Berlin: rot
    RGB(0, 80, 40)      // MV: grün
};

// Thread-Kommunikation
#define WM_BATCH_FORTSCHRITT (WM_USER + 1)
#define WM_BATCH_FERTIG      (WM_USER + 2)

struct BatchResult {
    int index;
    string these;
    string antwort;
    string begruendung;
};

vector<string> geladeneThesen;  // Thesen aus Datei

// Forward declarations
void AktualisiereLayout(HWND hwnd);

// Farbschema
#define COLOR_BG_DARK    RGB(26, 32, 44)
#define COLOR_BG_DARKER  RGB(23, 25, 35)
#define COLOR_BG_MEDIUM  RGB(45, 55, 72)
#define COLOR_BUTTON     RGB(49, 130, 206)
#define COLOR_BUTTON_HOV RGB(43, 108, 176)
#define COLOR_EXPORT     RGB(56, 161, 105)
#define COLOR_EXPORT_HOV RGB(47, 133, 90)
#define COLOR_TEXT       RGB(247, 250, 252)
#define COLOR_ZU         RGB(72, 187, 120)
#define COLOR_NICHT_ZU   RGB(252, 129, 74)

// -------------------------------------------------------
// Hilfsfunktionen
// -------------------------------------------------------

size_t WriteCallback(void* contents, size_t size, size_t nmemb, string* output) {
    output->append((char*)contents, size * nmemb);
    return size * nmemb;
}

string escapeJson(const string& str) {
    string escaped;
    for (unsigned char c : str) {
        switch (c) {
        case '"':  escaped += "\\\""; break;
        case '\\': escaped += "\\\\"; break;
        case '\n': escaped += "\\n";  break;
        case '\r': escaped += "\\r";  break;
        case '\t': escaped += "\\t";  break;
        default:
            if (c < 32) {
                char buf[8];
                sprintf_s(buf, "\\u%04x", (unsigned int)c);
                escaped += buf;
            }
            else {
                escaped += c;
            }
        }
    }
    return escaped;
}

string extractJsonString(const string& json, const string& key) {
    string searchKey = "\"" + key + "\"";
    size_t pos = json.find(searchKey);
    if (pos == string::npos) return "";
    pos = json.find(":", pos);
    if (pos == string::npos) return "";
    pos = json.find("\"", pos);
    if (pos == string::npos) return "";
    pos++;
    string result;
    while (pos < json.length()) {
        if (json[pos] == '\\' && pos + 1 < json.length()) {
            char next = json[pos + 1];
            if (next == '"') { result += '"';  pos += 2; }
            else if (next == 'n') { result += '\n'; pos += 2; }
            else if (next == 'r') { pos += 2; }
            else if (next == 't') { result += '\t'; pos += 2; }
            else if (next == '\\') { result += '\\'; pos += 2; }
            else if (next == 'u' && pos + 5 < json.length()) {
                string hex = json.substr(pos + 2, 4);
                bool ok = true;
                for (char c : hex) if (!isxdigit((unsigned char)c)) { ok = false; break; }
                if (ok) {
                    unsigned int cp = stoul(hex, nullptr, 16);
                    if (cp <= 0x7F)       result += (char)cp;
                    else if (cp <= 0x7FF) { result += (char)(0xC0 | (cp >> 6)); result += (char)(0x80 | (cp & 0x3F)); }
                    else { result += (char)(0xE0 | (cp >> 12)); result += (char)(0x80 | ((cp >> 6) & 0x3F)); result += (char)(0x80 | (cp & 0x3F)); }
                    pos += 6;
                }
                else { result += next; pos += 2; }
            }
            else { result += next; pos += 2; }
        }
        else if (json[pos] == '"') {
            break;
        }
        else {
            result += json[pos++];
        }
    }
    return result;
}

void AppendText(HWND hwnd, const string& text) {
    int len = GetWindowTextLengthW(hwnd);
    int wLen = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, NULL, 0);
    wchar_t* wText = new wchar_t[wLen];
    MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, wText, wLen);
    SendMessageW(hwnd, EM_SETSEL, len, len);
    SendMessageW(hwnd, EM_REPLACESEL, FALSE, (LPARAM)wText);
    SendMessageW(hwnd, EM_SCROLLCARET, 0, 0);
    delete[] wText;
}

// -------------------------------------------------------
// OpenAI Responses API mit Web Search
// -------------------------------------------------------

// Unicode \uXXXX -> UTF-8 konvertieren
string decodeUnicodeEscapes(const string& s) {
    string result;
    size_t i = 0;
    while (i < s.size()) {
        if (s[i] == '\\' && i + 5 < s.size() && s[i + 1] == 'u') {
            // \uXXXX lesen
            string hex = s.substr(i + 2, 4);
            bool valid = true;
            for (char c : hex) {
                if (!isxdigit((unsigned char)c)) { valid = false; break; }
            }
            if (valid) {
                unsigned int codepoint = stoul(hex, nullptr, 16);
                // UTF-8 encoding
                if (codepoint <= 0x7F) {
                    result += (char)codepoint;
                }
                else if (codepoint <= 0x7FF) {
                    result += (char)(0xC0 | (codepoint >> 6));
                    result += (char)(0x80 | (codepoint & 0x3F));
                }
                else {
                    result += (char)(0xE0 | (codepoint >> 12));
                    result += (char)(0x80 | ((codepoint >> 6) & 0x3F));
                    result += (char)(0x80 | (codepoint & 0x3F));
                }
                i += 6;
                continue;
            }
        }
        result += s[i++];
    }
    return result;
}

string extractOutputText(const string& json) {
    // Responses API Antwortstruktur:
    // {"id":"...","output":[{"type":"message","content":[{"type":"output_text","text":"..."}]}]}

    // Hilfslambda: liest einen JSON-String ab Position tq (nach öffnendem ")
    auto readJsonStr = [&](const string& j, size_t tq) -> string {
        string result;
        while (tq < j.length()) {
            if (j[tq] == '\\' && tq + 1 < j.length()) {
                char next = j[tq + 1];
                if (next == '"') { result += '"';  tq += 2; }
                else if (next == 'n') { result += '\n'; tq += 2; }
                else if (next == 'r') { tq += 2; }
                else if (next == 't') { result += '\t'; tq += 2; }
                else if (next == '\\') { result += '\\'; tq += 2; }
                else if (next == 'u' && tq + 5 < j.length()) {
                    // \uXXXX → UTF-8
                    string hex = j.substr(tq + 2, 4);
                    bool ok = true;
                    for (char c : hex) if (!isxdigit((unsigned char)c)) { ok = false; break; }
                    if (ok) {
                        unsigned int cp = stoul(hex, nullptr, 16);
                        if (cp <= 0x7F)       result += (char)cp;
                        else if (cp <= 0x7FF) { result += (char)(0xC0 | (cp >> 6)); result += (char)(0x80 | (cp & 0x3F)); }
                        else { result += (char)(0xE0 | (cp >> 12)); result += (char)(0x80 | ((cp >> 6) & 0x3F)); result += (char)(0x80 | (cp & 0x3F)); }
                        tq += 6;
                    }
                    else { result += next; tq += 2; }
                }
                else { result += next; tq += 2; }
            }
            else if (j[tq] == '"') {
                break;
            }
            else {
                result += j[tq++];
            }
        }
        return result;
        };

    // Strategie 1: Suche nach "type":"output_text" dann "text" danach
    {
        size_t typePos = 0;
        while (true) {
            typePos = json.find("\"type\"", typePos);
            if (typePos == string::npos) break;

            size_t colonPos = json.find(":", typePos);
            if (colonPos == string::npos) break;
            size_t valStart = json.find("\"", colonPos);
            if (valStart == string::npos) break;
            valStart++;
            size_t valEnd = json.find("\"", valStart);
            if (valEnd == string::npos) break;

            string typeVal = json.substr(valStart, valEnd - valStart);
            if (typeVal == "output_text") {
                size_t textPos = json.find("\"text\"", valEnd);
                if (textPos != string::npos && textPos < valEnd + 2000) {
                    size_t tc = json.find(":", textPos);
                    size_t tq = json.find("\"", tc);
                    if (tq != string::npos) {
                        string result = readJsonStr(json, tq + 1);
                        if (!result.empty()) return result;
                    }
                }
            }
            typePos = valEnd + 1;
        }
    }

    // Strategie 2: output[].content[].text
    {
        size_t outputPos = json.find("\"output\"");
        if (outputPos != string::npos) {
            size_t contentPos = json.find("\"content\"", outputPos);
            if (contentPos != string::npos) {
                size_t textPos = json.find("\"text\"", contentPos);
                if (textPos != string::npos) {
                    size_t tc = json.find(":", textPos);
                    size_t tq = json.find("\"", tc);
                    if (tq != string::npos) {
                        string result = readJsonStr(json, tq + 1);
                        if (!result.empty()) return result;
                    }
                }
            }
        }
    }

    // Strategie 3: Fallback Chat Completions "content" Feld
    {
        string content = extractJsonString(json, "content");
        if (!content.empty()) return content;
    }

    return "";
}

string beantworteThese(const string& these) {
    CURL* curl = curl_easy_init();
    if (!curl) return "{\"antwort\":\"Fehler\",\"begruendung\":\"CURL init fehlgeschlagen\"}";

    string wahlInfo = wahlKontext.empty() ? "allgemeine Wahl" : wahlKontext;

    string systemPrompt =
        "Du bist ein politisch ausgewogener Analyst, der Wahl-O-Mat-Thesen bewertet. "
        "Der Kontext dieser Befragung ist: " + wahlInfo + ".\n"
        "Nutze aktuelle Informationen aus dem Internet, um faktenbasiert zu antworten.\n"
        "Berücksichtige aktuelle politische Debatten, Wahlprogramme und gesellschaftliche Entwicklungen.\n\n"
        "ANTWORTFORMAT - Antworte IMMER NUR mit diesem JSON, nichts anderes:\n"
        "{\n"
        "  \"antwort\": \"Stimme zu\" ODER \"Stimme nicht zu\",\n"
        "  \"begruendung\": \"Deine Begründung in 2-3 Sätzen mit Bezug auf aktuelle Fakten\"\n"
        "}\n\n"
        "WICHTIGE REGELN:\n"
        "- Nur EXAKT \"Stimme zu\" oder \"Stimme nicht zu\" als Antwort\n"
        "- Begründung auf Deutsch, sachlich und klar\n"
        "- Keine parteipolitische Einfärbung\n"
        "- Aktuelle Daten und Fakten einbeziehen\n"
        "- Verwende korrekte deutsche Umlaute: ä, ö, ü, ß";

    // Responses API JSON mit web_search Tool
    string jsonPayload =
        "{"
        "\"model\":\"gpt-5.2\","
        "\"temperature\":0,"
        "\"top_p\":1,"
        "\"tools\":[{\"type\":\"web_search\"}],"
        "\"instructions\":\"" + escapeJson(systemPrompt) + "\","
        "\"input\":\"Recherchiere aktuelle Informationen und bewerte diese Wahl-O-Mat-These für " +
        escapeJson(wahlInfo) + ": " + escapeJson(these) + "\""
        "}";

    string responseData;

    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json; charset=utf-8");
    string authHeader = "Authorization: Bearer " + apiKey;
    headers = curl_slist_append(headers, authHeader.c_str());

    // Responses API Endpoint
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.openai.com/v1/responses");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonPayload.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);  // 60s Timeout für Web Search

    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        return "{\"antwort\":\"Fehler\",\"begruendung\":\"" + string(curl_easy_strerror(res)) + "\"}";
    }

    // Responses API: output_text Feld extrahieren
    string content = extractOutputText(responseData);

    if (content.empty()) {
        // Fallback: versuche content aus Chat Completions Format
        content = extractJsonString(responseData, "content");
    }

    if (content.empty()) {
        // Zeige die ersten 400 Zeichen der rohen Antwort für Debugging
        string raw = responseData.size() > 400 ? responseData.substr(0, 400) : responseData;
        return "{\"antwort\":\"Fehler\",\"begruendung\":\"Parser fehlgeschlagen. Raw: " +
            escapeJson(raw) + "\"}";
    }

    // JSON aus dem Content extrahieren
    size_t jsonStart = content.find('{');
    if (jsonStart != string::npos) {
        return content.substr(jsonStart);
    }

    return "{\"antwort\":\"Fehler\",\"begruendung\":\"JSON nicht gefunden in: " +
        escapeJson(content.substr(0, 200)) + "\"}";
}

// -------------------------------------------------------
// Export als TXT
// -------------------------------------------------------

void exportErgebnisse() {
    if (alleThesen.empty()) {
        MessageBoxW(NULL, L"Keine Thesen zum Exportieren vorhanden!", L"Hinweis", MB_OK | MB_ICONINFORMATION);
        return;
    }

    // Dateiname aus wahlKontext ableiten: Sonderzeichen ersetzen
    string safeName = wahlKontext;
    for (char& c : safeName) {
        if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
            c == '"' || c == '<' || c == '>' || c == '|')
            c = '_';
    }
    string filename = "Wahlomat_" + safeName + ".txt";

    ofstream file(filename, ios::out | ios::trunc);
    if (!file.is_open()) {
        MessageBoxW(NULL, L"Datei konnte nicht erstellt werden!", L"Fehler", MB_OK | MB_ICONERROR);
        return;
    }

    file << "========================================\n";
    file << "WAHL-O-MAT KI - ERGEBNISSE\n";
    file << "Wahl: " << wahlKontext << "\n";
    file << "========================================\n\n";

    int stimmeZu = 0, stimmeNichtZu = 0;

    for (size_t i = 0; i < alleThesen.size(); i++) {
        file << "These " << (i + 1) << ":\n";
        file << alleThesen[i].text << "\n\n";
        file << "Antwort: " << alleThesen[i].antwort << "\n";
        file << "Begr\xC3\xBCndung: " << alleThesen[i].begruendung << "\n";
        file << "----------------------------------------\n\n";

        if (alleThesen[i].antwort == "Stimme zu") stimmeZu++;
        else stimmeNichtZu++;
    }

    file << "========================================\n";
    file << "ZUSAMMENFASSUNG:\n";
    file << "Stimme zu:       " << stimmeZu << " von " << alleThesen.size() << "\n";
    file << "Stimme nicht zu: " << stimmeNichtZu << " von " << alleThesen.size() << "\n";
    file << "========================================\n\n";

    // Kurzübersicht: nur These-Nr. + Ergebnis
    file << "KURZÜBERSICHT:\n";
    file << "----------------------------------------\n";
    for (size_t i = 0; i < alleThesen.size(); i++) {
        string symbol = (alleThesen[i].antwort == "Stimme zu") ? "[+]" : "[-]";
        file << symbol << " These " << (i + 1) << ": " << alleThesen[i].antwort << "\n";
    }
    file << "========================================\n";

    file.close();

    // Dateiname im Popup anzeigen
    int wLen = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, NULL, 0);
    wchar_t* wFilename = new wchar_t[wLen];
    MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wFilename, wLen);
    wstring msg = wstring(L"Ergebnisse wurden gespeichert:\n") + wFilename;
    MessageBoxW(NULL, msg.c_str(), L"Export erfolgreich", MB_OK | MB_ICONINFORMATION);
    delete[] wFilename;
}

// -------------------------------------------------------
// Thesen-Datei laden
// -------------------------------------------------------

void DateiLaden(HWND hwnd) {
    OPENFILENAMEW ofn = {};
    wchar_t szFile[512] = {};

    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = 512;
    ofn.lpstrFilter = L"Textdateien (*.txt)\0*.txt\0Alle Dateien (*.*)\0*.*\0";
    ofn.lpstrTitle = L"Thesen-Datei auswählen";
    ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;

    if (!GetOpenFileNameW(&ofn)) return;

    // _wfopen für Unicode-Pfade (Umlaute im Pfad kein Problem)
    FILE* fp = _wfopen(szFile, L"rb");
    if (!fp) {
        MessageBoxW(hwnd, L"Datei konnte nicht geöffnet werden!", L"Fehler", MB_OK | MB_ICONERROR);
        return;
    }

    geladeneThesen.clear();
    string line;
    string rawContent;

    // Datei als Bytes lesen
    char buf[4096];
    size_t n;
    while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
        rawContent.append(buf, n);
    fclose(fp);

    // BOM überspringen falls vorhanden (UTF-8 BOM: EF BB BF)
    size_t startPos = 0;
    if (rawContent.size() >= 3 &&
        (unsigned char)rawContent[0] == 0xEF &&
        (unsigned char)rawContent[1] == 0xBB &&
        (unsigned char)rawContent[2] == 0xBF)
        startPos = 3;

    // Zeilenweise aufteilen
    string current;
    for (size_t i = startPos; i < rawContent.size(); i++) {
        char c = rawContent[i];
        if (c == '\r') continue;  // \r ignorieren
        if (c == '\n') {
            // Zeile verarbeiten
            // \r am Ende entfernen (falls doch noch da)
            while (!current.empty() && current.back() == '\r') current.pop_back();

            if (!current.empty() && isdigit((unsigned char)current[0])) {
                // Nummer + Punkt/Klammer entfernen: "1. " oder "10. "
                size_t dotPos = current.find_first_of(".)");
                if (dotPos != string::npos && dotPos <= 3) {
                    size_t s = dotPos + 1;
                    while (s < current.size() && current[s] == ' ') s++;
                    current = current.substr(s);
                }
                if (!current.empty())
                    geladeneThesen.push_back(current);
            }
            current.clear();
        }
        else {
            current += c;
        }
    }
    // Letzte Zeile
    if (!current.empty() && isdigit((unsigned char)current[0])) {
        size_t dotPos = current.find_first_of(".)");
        if (dotPos != string::npos && dotPos <= 3) {
            size_t s = dotPos + 1;
            while (s < current.size() && current[s] == ' ') s++;
            current = current.substr(s);
        }
        if (!current.empty()) geladeneThesen.push_back(current);
    }

    if (geladeneThesen.empty()) {
        MessageBoxW(hwnd, L"Keine Thesen in der Datei gefunden!", L"Hinweis", MB_OK | MB_ICONWARNING);
        return;
    }

    // Bestätigung anzeigen
    wchar_t msg[256];
    swprintf_s(msg, L"%d Thesen geladen!\n\nJetzt auf 'Alle analysieren' klicken.", (int)geladeneThesen.size());
    MessageBoxW(hwnd, msg, L"Datei geladen", MB_OK | MB_ICONINFORMATION);

    // Info im Chat
    string info = ">>> Datei geladen: " + to_string(geladeneThesen.size()) + " Thesen\r\n";
    info += ">>> Klicke 'Alle analysieren' um den Batch-Modus zu starten.\r\n";
    info += ">>> (Ca. " + to_string(geladeneThesen.size() * 15) + " Sekunden Gesamtdauer geschätzt)\r\n\r\n";
    AppendText(hChatDisplay, info);

    ShowWindow(hBatchButton, SW_SHOW);
}

// -------------------------------------------------------
// Batch-Thread
// -------------------------------------------------------

struct BatchThreadParam {
    HWND hwnd;
    vector<string> thesen;
};

DWORD WINAPI BatchThread(LPVOID lpParam) {
    BatchThreadParam* param = (BatchThreadParam*)lpParam;
    HWND hwnd = param->hwnd;
    vector<string> thesen = param->thesen;
    delete param;

    for (size_t i = 0; i < thesen.size(); i++) {
        // Fortschritt senden
        string* thesePtr = new string(thesen[i]);
        PostMessage(hwnd, WM_BATCH_FORTSCHRITT, (WPARAM)i, (LPARAM)thesePtr);

        // API anfragen
        string jsonAntwort = beantworteThese(thesen[i]);
        string antwort = extractJsonString(jsonAntwort, "antwort");
        string begruendung = extractJsonString(jsonAntwort, "begruendung");
        if (antwort.empty())     antwort = "Fehler";
        if (begruendung.empty()) begruendung = jsonAntwort;

        // Ergebnis zurückschicken
        BatchResult* result = new BatchResult();
        result->index = (int)i;
        result->these = thesen[i];
        result->antwort = antwort;
        result->begruendung = begruendung;
        // wParam = 1 wenn letzte These
        PostMessage(hwnd, WM_BATCH_FERTIG, (WPARAM)(i + 1 == thesen.size() ? 1 : 0), (LPARAM)result);

        // Pause zwischen Anfragen (API Rate-Limit)
        if (i + 1 < thesen.size()) Sleep(1500);
    }

    return 0;
}

void BatchAnalyse(HWND hwnd) {
    if (geladeneThesen.empty()) {
        MessageBoxW(hwnd, L"Bitte zuerst eine Datei laden!", L"Hinweis", MB_OK | MB_ICONWARNING);
        return;
    }
    if (batchLaeuft) return;

    batchLaeuft = true;
    EnableWindow(hBatchButton, FALSE);
    EnableWindow(hSendButton, FALSE);
    EnableWindow(hDateiButton, FALSE);

    string header = "========================================\r\n";
    header += "BATCH-ANALYSE: " + to_string(geladeneThesen.size()) + " Thesen\r\n";
    header += "========================================\r\n\r\n";
    AppendText(hChatDisplay, header);

    BatchThreadParam* param = new BatchThreadParam();
    param->hwnd = hwnd;
    param->thesen = geladeneThesen;

    HANDLE hThread = CreateThread(NULL, 0, BatchThread, param, 0, NULL);
    if (hThread) CloseHandle(hThread);
}

// -------------------------------------------------------
// Zurück zum Startbildschirm
// -------------------------------------------------------

void ZurueckZumStart(HWND hwnd) {
    if (batchLaeuft) {
        MessageBoxW(hwnd,
            L"Bitte warte bis die Batch-Analyse abgeschlossen ist!",
            L"Analyse läuft", MB_OK | MB_ICONWARNING);
        return;
    }

    // Alles zurücksetzen
    wahlKontext = "";
    wahlFestgelegt = false;
    batchLaeuft = false;
    alleThesen.clear();
    geladeneThesen.clear();

    // Eingabefelder leeren
    SetWindowTextW(hWahlInput, L"");
    SetWindowTextW(hTheseInput, L"");
    SetWindowTextW(hChatDisplay, L"");

    // Haupt-UI verstecken
    ShowWindow(hChatDisplay, SW_HIDE);
    ShowWindow(hTheseInput, SW_HIDE);
    ShowWindow(hSendButton, SW_HIDE);
    ShowWindow(hDateiButton, SW_HIDE);
    ShowWindow(hBatchButton, SW_HIDE);
    ShowWindow(hExportButton, SW_HIDE);
    ShowWindow(hResetButton, SW_HIDE);

    // Startbildschirm einblenden
    ShowWindow(hWahlPanel, SW_SHOW);
    ShowWindow(hWahlLabel, SW_SHOW);
    ShowWindow(hWahlButton, SW_SHOW);
    for (int i = 0; i < 5; i++) ShowWindow(hWahlKarten[i], SW_SHOW);

    // Layout für Startbildschirm neu berechnen
    AktualisiereLayout(hwnd);
    SetFocus(hWahlInput);
}

// -------------------------------------------------------
// Wahl festlegen
// -------------------------------------------------------

void FestlegenWahl(HWND hwnd) {
    wchar_t buffer[512];
    GetWindowTextW(hWahlInput, buffer, 512);

    int utf8Len = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
    char* utf8Text = new char[utf8Len];
    WideCharToMultiByte(CP_UTF8, 0, buffer, -1, utf8Text, utf8Len, NULL, NULL);
    wahlKontext = utf8Text;
    delete[] utf8Text;

    if (wahlKontext.empty()) return;

    wahlFestgelegt = true;

    // Wahl-Panel ausblenden, Hauptbereich einblenden
    ShowWindow(hWahlPanel, SW_HIDE);
    ShowWindow(hWahlLabel, SW_HIDE);
    ShowWindow(hWahlButton, SW_HIDE);
    for (int i = 0; i < 5; i++) ShowWindow(hWahlKarten[i], SW_HIDE);
    ShowWindow(hChatDisplay, SW_SHOW);
    ShowWindow(hTheseInput, SW_SHOW);
    ShowWindow(hSendButton, SW_SHOW);
    ShowWindow(hDateiButton, SW_SHOW);
    ShowWindow(hExportButton, SW_SHOW);
    ShowWindow(hResetButton, SW_SHOW);
    // hBatchButton erst nach Datei laden sichtbar

    // Layout für Haupt-UI neu berechnen
    AktualisiereLayout(hwnd);

    // Willkommensnachricht mit Wahlkontext
    string intro = "=== WAHL-O-MAT KI ===\r\n";
    intro += "Wahl: " + wahlKontext + "\r\n";
    intro += "________________________________________\r\n";
    intro += "Gib jetzt die Thesen ein. Die KI bewertet sie\r\n";
    intro += "im Kontext dieser Wahl.\r\n";
    intro += "________________________________________\r\n\r\n";
    AppendText(hChatDisplay, intro);

    SetFocus(hTheseInput);
}

// -------------------------------------------------------
// Verarbeitung einer These
// -------------------------------------------------------

void VerarbeiteThese(HWND hwnd) {
    wchar_t buffer[1024];
    GetWindowTextW(hTheseInput, buffer, 1024);

    int utf8Len = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
    char* utf8Text = new char[utf8Len];
    WideCharToMultiByte(CP_UTF8, 0, buffer, -1, utf8Text, utf8Len, NULL, NULL);
    string theseText = utf8Text;
    delete[] utf8Text;

    if (theseText.empty()) return;
    SetWindowTextW(hTheseInput, L"");

    // These anzeigen
    string theseNr = "These " + to_string(alleThesen.size() + 1) + ":\r\n";
    AppendText(hChatDisplay, theseNr + theseText + "\r\n\r\n");
    AppendText(hChatDisplay, "Recherchiere im Internet und analysiere...\r\n\r\n");

    SetCursor(LoadCursor(NULL, IDC_WAIT));
    UpdateWindow(hwnd);

    // KI anfragen
    string jsonAntwort = beantworteThese(theseText);

    SetCursor(LoadCursor(NULL, IDC_ARROW));

    // JSON parsen
    string antwort = extractJsonString(jsonAntwort, "antwort");
    string begruendung = extractJsonString(jsonAntwort, "begruendung");

    if (antwort.empty()) antwort = "Fehler";
    if (begruendung.empty()) begruendung = jsonAntwort;

    // Ergebnis speichern
    alleThesen.push_back({ theseText, antwort, begruendung });

    // Anzeige aktualisieren - letzte "Analysiere..." Zeile entfernen
    // Ergebnis anzeigen
    string ergebnis = ">> " + antwort + "\r\n";
    ergebnis += "Begründung: " + begruendung + "\r\n";
    ergebnis += "________________________________________\r\n\r\n";
    AppendText(hChatDisplay, ergebnis);

    SetFocus(hTheseInput);
}

// -------------------------------------------------------
// Responsives Layout - wird bei WM_CREATE und WM_SIZE aufgerufen
// -------------------------------------------------------

void AktualisiereLayout(HWND hwnd) {
    RECT rc;
    GetClientRect(hwnd, &rc);
    int W = rc.right;
    int H = rc.bottom;

    const int BTN_H = 32;
    const int MARGIN = 10;
    const int BAR_Y = H - BTN_H - 8;

    if (wahlFestgelegt) {
        // === HAUPT-UI: Chat füllt alles, Buttons unten ===
        SetWindowPos(hChatDisplay, NULL,
            MARGIN, MARGIN,
            W - 2 * MARGIN, BAR_Y - MARGIN - 4,
            SWP_NOZORDER);

        int inputW = (W - 2 * MARGIN) * 44 / 100;
        SetWindowPos(hTheseInput, NULL, MARGIN, BAR_Y, inputW, BTN_H, SWP_NOZORDER);

        int btnAreaX = MARGIN + inputW + 6;
        int btnAreaW = W - btnAreaX - MARGIN;
        int btnW = (btnAreaW - 4 * 4) / 5;
        HWND btns[] = { hSendButton, hDateiButton, hBatchButton, hExportButton, hResetButton };
        for (int i = 0; i < 5; i++)
            SetWindowPos(btns[i], NULL, btnAreaX + i * (btnW + 4), BAR_Y, btnW, BTN_H, SWP_NOZORDER);

    }
    else {
        // === STARTBILDSCHIRM ===
        // Titel-Label: Zeile bei ~28% Fensterhöhe, volle Breite
        int labelY = H * 28 / 100;
        SetWindowPos(hWahlLabel, NULL, W / 8, labelY, W * 6 / 8, 28, SWP_NOZORDER);

        // Kacheln: responsive Größe, gleichmäßig verteilt
        // Verfügbare Höhe für Karten: von ~33% bis ~78%
        int kartenZone = H * 78 / 100 - H * 33 / 100;  // ~45% der Höhe
        const int GAP = 14;
        int KARTEN_H = (kartenZone - GAP) / 2;  // 2 Zeilen
        if (KARTEN_H < 70) KARTEN_H = 70;
        if (KARTEN_H > 130) KARTEN_H = 130;

        // Karten-Breite: 3 pro Zeile, mit Seitenabstand 8%
        int KARTEN_W = (W * 84 / 100 - 2 * GAP) / 3;
        if (KARTEN_W < 120) KARTEN_W = 120;

        int row1Y = H * 33 / 100;
        int row1TotalW = 3 * KARTEN_W + 2 * GAP;
        int row1X = (W - row1TotalW) / 2;
        for (int i = 0; i < 3; i++)
            SetWindowPos(hWahlKarten[i], NULL,
                row1X + i * (KARTEN_W + GAP), row1Y,
                KARTEN_W, KARTEN_H, SWP_NOZORDER);

        int row2Y = row1Y + KARTEN_H + GAP;
        int row2TotalW = 2 * KARTEN_W + GAP;
        int row2X = (W - row2TotalW) / 2;
        for (int i = 3; i < 5; i++)
            SetWindowPos(hWahlKarten[i], NULL,
                row2X + (i - 3) * (KARTEN_W + GAP), row2Y,
                KARTEN_W, KARTEN_H, SWP_NOZORDER);

        // Manuelle Eingabe: unterhalb bei ~82%
        int manualY = H * 82 / 100;
        int inputW = W * 52 / 100;
        int inputX = (W - inputW - 120) / 2;
        SetWindowPos(hHintLabel, NULL, inputX, manualY - 20, 220, 18, SWP_NOZORDER);
        SetWindowPos(hWahlInput, NULL, inputX, manualY, inputW, 32, SWP_NOZORDER);
        SetWindowPos(hWahlButton, NULL, inputX + inputW + 8, manualY, 112, 32, SWP_NOZORDER);
    }

    InvalidateRect(hwnd, NULL, TRUE);
}

// -------------------------------------------------------
// Enter-Taste im Eingabefeld
// -------------------------------------------------------

WNDPROC oldEditProc;
LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_KEYDOWN && wParam == VK_RETURN) {
        VerarbeiteThese(GetParent(hwnd));
        return 0;
    }
    return CallWindowProc(oldEditProc, hwnd, uMsg, wParam, lParam);
}

WNDPROC oldWahlEditProc;
LRESULT CALLBACK WahlEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_KEYDOWN && wParam == VK_RETURN) {
        FestlegenWahl(GetParent(hwnd));
        return 0;
    }
    return CallWindowProc(oldWahlEditProc, hwnd, uMsg, wParam, lParam);
}

// -------------------------------------------------------
// Window Procedure
// -------------------------------------------------------

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE: {
        hBackgroundBrush = CreateSolidBrush(COLOR_BG_DARK);
        hChatBrush = CreateSolidBrush(COLOR_BG_DARKER);
        hInputBrush = CreateSolidBrush(COLOR_BG_MEDIUM);
        hScrollBrush = CreateSolidBrush(RGB(45, 55, 72));

        hFont = CreateFontW(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI");

        hFontBold = CreateFontW(20, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI");

        // === WAHL-AUSWAHL PANEL (Startbildschirm) ===
        hWahlLabel = CreateWindowW(L"STATIC",
            L"Welche Wahl soll bewertet werden?",
            WS_CHILD | WS_VISIBLE | SS_CENTER,
            0, 0, 100, 30,  // Position via WM_SIZE
            hwnd, NULL, NULL, NULL);
        SendMessageW(hWahlLabel, WM_SETFONT, (WPARAM)hFontBold, TRUE);

        const wchar_t* wahlLabels[5] = {
            L"Landtagswahl\nBaden-W\u00FCrttemberg\n8. M\u00E4rz 2026",
            L"Landtagswahl\nRheinland-Pfalz\n22. M\u00E4rz 2026",
            L"Landtagswahl\nSachsen-Anhalt\n6. Sept. 2026",
            L"Abgeordnetenhaus-\nwahl Berlin\n20. Sept. 2026",
            L"Landtagswahl\nMeckl.-Vorpommern\n20. Sept. 2026"
        };
        for (int i = 0; i < 5; i++) {
            hWahlKarten[i] = CreateWindowW(L"BUTTON", wahlLabels[i],
                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW | BS_MULTILINE,
                0, 0, 10, 10,  // Position via WM_SIZE
                hwnd, (HMENU)(10 + i), NULL, NULL);
            SendMessageW(hWahlKarten[i], WM_SETFONT, (WPARAM)hFont, TRUE);
        }

        hHintLabel = CreateWindowW(L"STATIC", L"Oder manuell eingeben:",
            WS_CHILD | WS_VISIBLE | SS_LEFT,
            0, 0, 220, 18,
            hwnd, NULL, NULL, NULL);
        SendMessageW(hHintLabel, WM_SETFONT, (WPARAM)hFont, TRUE);

        hWahlInput = CreateWindowExW(
            0, L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
            0, 0, 100, 32,
            hwnd, (HMENU)5, NULL, NULL);
        SendMessageW(hWahlInput, WM_SETFONT, (WPARAM)hFont, TRUE);
        oldWahlEditProc = (WNDPROC)SetWindowLongPtr(hWahlInput, GWLP_WNDPROC, (LONG_PTR)WahlEditSubclassProc);

        hWahlButton = CreateWindowW(L"BUTTON", L"Los geht's",
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 100, 32,
            hwnd, (HMENU)6, NULL, NULL);
        SendMessageW(hWahlButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        hWahlPanel = hWahlInput;

        // === HAUPT-UI (zunächst versteckt) ===
        hChatDisplay = CreateWindowExW(
            0, L"EDIT", L"",
            WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
            0, 0, 100, 100,
            hwnd, (HMENU)3, NULL, NULL);
        SendMessageW(hChatDisplay, WM_SETFONT, (WPARAM)hFont, TRUE);
        // Dunkler Scrollbalken via Dark-Mode
        SetWindowTheme(hChatDisplay, L"DarkMode_Explorer", NULL);
        // Alternativ: eigene Scrollbar-Farbe über Window-Attribute
        BOOL darkMode = TRUE;
        DwmSetWindowAttribute(hChatDisplay, 20 /*DWMWA_USE_IMMERSIVE_DARK_MODE*/, &darkMode, sizeof(darkMode));

        hTheseInput = CreateWindowExW(
            0, L"EDIT", L"",
            WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
            0, 0, 100, 32,
            hwnd, (HMENU)1, NULL, NULL);
        SendMessageW(hTheseInput, WM_SETFONT, (WPARAM)hFont, TRUE);
        oldEditProc = (WNDPROC)SetWindowLongPtr(hTheseInput, GWLP_WNDPROC, (LONG_PTR)EditSubclassProc);

        hSendButton = CreateWindowW(L"BUTTON", L"Bewerten",
            WS_CHILD | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 78, 32, hwnd, (HMENU)2, NULL, NULL);
        SendMessageW(hSendButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        hDateiButton = CreateWindowW(L"BUTTON", L"Datei laden",
            WS_CHILD | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 78, 32, hwnd, (HMENU)7, NULL, NULL);
        SendMessageW(hDateiButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        hBatchButton = CreateWindowW(L"BUTTON", L"Alle analysieren",
            WS_CHILD | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 78, 32, hwnd, (HMENU)8, NULL, NULL);
        SendMessageW(hBatchButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        hExportButton = CreateWindowW(L"BUTTON", L"Exportieren",
            WS_CHILD | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 78, 32, hwnd, (HMENU)4, NULL, NULL);
        SendMessageW(hExportButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        hResetButton = CreateWindowW(L"BUTTON", L"\u21A9 Neue Wahl",
            WS_CHILD | BS_PUSHBUTTON | BS_OWNERDRAW,
            0, 0, 78, 32, hwnd, (HMENU)9, NULL, NULL);
        SendMessageW(hResetButton, WM_SETFONT, (WPARAM)hFont, TRUE);

        SetFocus(hWahlInput);
        // Initiales Layout berechnen
        AktualisiereLayout(hwnd);
        break;
    }

    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLOREDIT: {
        HDC hdcEdit = (HDC)wParam;
        HWND hwndEdit = (HWND)lParam;
        if (hwndEdit == hChatDisplay) {
            SetTextColor(hdcEdit, RGB(255, 255, 255));
            SetBkColor(hdcEdit, RGB(23, 25, 35));
            return (LRESULT)hChatBrush;
        }
        else if (hwndEdit == hTheseInput || hwndEdit == hWahlInput) {
            SetTextColor(hdcEdit, RGB(255, 255, 255));
            SetBkColor(hdcEdit, RGB(45, 55, 72));
            return (LRESULT)hInputBrush;
        }
        else if (hwndEdit == hWahlLabel || hwndEdit == hHintLabel) {
            SetTextColor(hdcEdit, RGB(160, 174, 192));
            SetBkColor(hdcEdit, COLOR_BG_DARK);
            return (LRESULT)hBackgroundBrush;
        }
        break;
    }

    case WM_DRAWITEM: {
        LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
        HDC hdc = pDIS->hDC;
        RECT rect = pDIS->rcItem;

        COLORREF btnColor, hoverColor;
        const wchar_t* label;

        if (pDIS->CtlID == 2) {
            btnColor = COLOR_BUTTON;
            hoverColor = COLOR_BUTTON_HOV;
            label = L"Bewerten";
        }
        else if (pDIS->CtlID == 4) {
            btnColor = COLOR_EXPORT;
            hoverColor = COLOR_EXPORT_HOV;
            label = L"Exportieren";
        }
        else if (pDIS->CtlID == 6) {
            btnColor = COLOR_EXPORT;
            hoverColor = COLOR_EXPORT_HOV;
            label = L"Los geht's";
        }
        else if (pDIS->CtlID == 7) {
            btnColor = RGB(113, 128, 150);
            hoverColor = RGB(74, 85, 104);
            label = L"Datei laden";
        }
        else if (pDIS->CtlID == 8) {
            btnColor = RGB(214, 158, 46);
            hoverColor = RGB(183, 121, 31);
            label = L"Alle analysieren";
        }
        else if (pDIS->CtlID == 9) {
            btnColor = RGB(160, 60, 60);
            hoverColor = RGB(120, 40, 40);
            label = L"\u21A9 Neue Wahl";
        }
        else if (pDIS->CtlID >= 10 && pDIS->CtlID <= 14) {
            // Wahl-Karte
            int idx = pDIS->CtlID - 10;
            COLORREF base = wahlFarben[idx];
            bool hover = (pDIS->itemState & ODS_SELECTED) || (pDIS->itemState & ODS_FOCUS);

            // Hintergrund
            COLORREF bgColor = hover
                ? RGB(GetRValue(base) + 40, GetGValue(base) + 40, GetBValue(base) + 40)
                : RGB(GetRValue(base) / 2 + 20, GetGValue(base) / 2 + 20, GetBValue(base) / 2 + 20);
            // Clamp
            auto clamp = [](int v) { return v > 255 ? 255 : v; };
            bgColor = hover
                ? RGB(clamp(GetRValue(base) + 40), clamp(GetGValue(base) + 40), clamp(GetBValue(base) + 40))
                : RGB(GetRValue(base) / 3 + 30, GetGValue(base) / 3 + 30, GetBValue(base) / 3 + 30);

            HBRUSH hBg = CreateSolidBrush(bgColor);
            FillRect(hdc, &rect, hBg);
            DeleteObject(hBg);

            // Farbiger Rand oben (4px)
            RECT topBar = { rect.left, rect.top, rect.right, rect.top + 4 };
            HBRUSH hBar = CreateSolidBrush(base);
            FillRect(hdc, &topBar, hBar);
            DeleteObject(hBar);

            // Rahmen
            HPEN hPen = CreatePen(PS_SOLID, 1, hover ? base : RGB(60, 70, 90));
            HPEN hOld = (HPEN)SelectObject(hdc, hPen);
            SelectObject(hdc, GetStockObject(NULL_BRUSH));
            Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
            SelectObject(hdc, hOld);
            DeleteObject(hPen);

            // Text
            wchar_t buf[256];
            GetWindowTextW(pDIS->hwndItem, buf, 256);
            SetTextColor(hdc, RGB(247, 250, 252));
            SetBkMode(hdc, TRANSPARENT);
            SelectObject(hdc, hFont);
            RECT textRect = { rect.left + 8, rect.top + 10, rect.right - 8, rect.bottom - 6 };
            DrawTextW(hdc, buf, -1, &textRect, DT_CENTER | DT_WORDBREAK);
            return TRUE;
        }
        else break;

        HBRUSH hBrush = CreateSolidBrush(
            (pDIS->itemState & ODS_SELECTED) ? hoverColor : btnColor);
        FillRect(hdc, &rect, hBrush);
        DeleteObject(hBrush);

        SetTextColor(hdc, COLOR_TEXT);
        SetBkMode(hdc, TRANSPARENT);
        SelectObject(hdc, hFont);
        DrawTextW(hdc, label, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        return TRUE;
    }

    case WM_COMMAND: {
        if (LOWORD(wParam) == 6) {
            FestlegenWahl(hwnd);
        }
        else if (LOWORD(wParam) == 2) {
            VerarbeiteThese(hwnd);
        }
        else if (LOWORD(wParam) == 4) {
            exportErgebnisse();
        }
        else if (LOWORD(wParam) == 7) {
            DateiLaden(hwnd);
        }
        else if (LOWORD(wParam) == 8) {
            BatchAnalyse(hwnd);
        }
        else if (LOWORD(wParam) == 9) {
            ZurueckZumStart(hwnd);
        }
        else if (LOWORD(wParam) >= 10 && LOWORD(wParam) <= 14) {
            // Wahl-Karte angeklickt → Kontext direkt setzen und starten
            int idx = LOWORD(wParam) - 10;
            wahlKontext = wahlProfile[idx];
            // Eingabefeld auch füllen (als Anzeige)
            int wLen = MultiByteToWideChar(CP_UTF8, 0, wahlKontext.c_str(), -1, NULL, 0);
            wchar_t* wBuf = new wchar_t[wLen];
            MultiByteToWideChar(CP_UTF8, 0, wahlKontext.c_str(), -1, wBuf, wLen);
            SetWindowTextW(hWahlInput, wBuf);
            delete[] wBuf;
            FestlegenWahl(hwnd);
        }
        break;
    }

    case WM_BATCH_FORTSCHRITT: {
        // These wird gerade verarbeitet
        string* thesePtr = (string*)lParam;
        int idx = (int)wParam;
        string msg = ">>> These " + to_string(idx + 1) + "/" +
            to_string(geladeneThesen.size()) + ": " + *thesePtr + "\r\n";
        msg += "    Recherchiere...\r\n";
        AppendText(hChatDisplay, msg);
        delete thesePtr;
        return 0;
    }

    case WM_BATCH_FERTIG: {
        // Ergebnis einer These
        BatchResult* result = (BatchResult*)lParam;
        bool letzteThese = (wParam == 1);

        // Ergebnis speichern
        alleThesen.push_back({ result->these, result->antwort, result->begruendung });

        // Anzeige
        string antwortFarbe = (result->antwort == "Stimme zu") ? "++ " : "-- ";
        string ausgabe = "    " + antwortFarbe + result->antwort + "\r\n";
        ausgabe += "    " + result->begruendung + "\r\n";
        ausgabe += "    ________________________________________\r\n\r\n";
        AppendText(hChatDisplay, ausgabe);

        delete result;

        if (letzteThese) {
            // Batch fertig
            batchLaeuft = false;
            EnableWindow(hBatchButton, TRUE);
            EnableWindow(hSendButton, TRUE);
            EnableWindow(hDateiButton, TRUE);

            // Zusammenfassung
            int zu = 0, nichtZu = 0;
            for (auto& t : alleThesen) {
                if (t.antwort == "Stimme zu") zu++;
                else nichtZu++;
            }
            string summary = "========================================\r\n";
            summary += "BATCH ABGESCHLOSSEN!\r\n";
            summary += "Stimme zu:       " + to_string(zu) + "\r\n";
            summary += "Stimme nicht zu: " + to_string(nichtZu) + "\r\n";
            summary += "========================================\r\n\r\n";
            AppendText(hChatDisplay, summary);

            // Automatisch exportieren
            exportErgebnisse();
        }
        return 0;
    }

    case WM_SIZE: {
        if (wParam != SIZE_MINIMIZED)
            AktualisiereLayout(hwnd);
        break;
    }

    case WM_CTLCOLORSCROLLBAR: {
        HDC hdc = (HDC)wParam;
        SetDCBrushColor(hdc, RGB(45, 55, 72));
        return (LRESULT)hScrollBrush;
    }

    case WM_PAINT: {
        if (!wahlFestgelegt) {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            RECT rcClient;
            GetClientRect(hwnd, &rcClient);
            int W = rcClient.right;
            int H = rcClient.bottom;

            FillRect(hdc, &rcClient, hBackgroundBrush);

            // Akzentlinie oben
            RECT rcLine = { 0, 0, W, 5 };
            HBRUSH hAccent = CreateSolidBrush(COLOR_BUTTON);
            FillRect(hdc, &rcLine, hAccent);
            DeleteObject(hAccent);

            // Trennlinie unter Untertitel (bei ~H*0.27)
            int divY1 = H * 27 / 100;
            RECT rcDiv1 = { W / 8, divY1, W * 7 / 8, divY1 + 2 };
            HBRUSH hDiv1 = CreateSolidBrush(RGB(74, 85, 104));
            FillRect(hdc, &rcDiv1, hDiv1);
            DeleteObject(hDiv1);

            // Trennlinie über manuelle Eingabe (bei ~H*0.78)
            int divY2 = H * 78 / 100;
            RECT rcDiv2 = { W / 8, divY2, W * 7 / 8, divY2 + 2 };
            HBRUSH hDiv2 = CreateSolidBrush(RGB(74, 85, 104));
            FillRect(hdc, &rcDiv2, hDiv2);
            DeleteObject(hDiv2);

            // Großer Titel (zentriert, oberes Viertel)
            SelectObject(hdc, hFontBold);
            SetTextColor(hdc, COLOR_BUTTON);
            SetBkMode(hdc, TRANSPARENT);
            RECT rcTitle = { 0, H * 5 / 100, W, H * 16 / 100 };
            DrawTextW(hdc, L"WAHL-O-MAT KI", -1, &rcTitle, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

            // Untertitel
            SelectObject(hdc, hFont);
            SetTextColor(hdc, RGB(160, 174, 192));
            RECT rcSub = { 0, H * 17 / 100, W, H * 26 / 100 };
            DrawTextW(hdc, L"KI-gest\u00FCtzter These-Bewerter", -1, &rcSub, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

            EndPaint(hwnd, &ps);
            return 0;
        }
        break;
    }

    case WM_ERASEBKGND: {
        HDC hdc = (HDC)wParam;
        RECT rc;
        GetClientRect(hwnd, &rc);
        FillRect(hdc, &rc, hBackgroundBrush);
        return 1;
    }

    case WM_DESTROY:
        if (hFont) DeleteObject(hFont);
        if (hFontBold) DeleteObject(hFontBold);
        if (hBackgroundBrush) DeleteObject(hBackgroundBrush);
        if (hChatBrush) DeleteObject(hChatBrush);
        if (hInputBrush) DeleteObject(hInputBrush);
        if (hScrollBrush) DeleteObject(hScrollBrush);
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// -------------------------------------------------------
// WinMain
// -------------------------------------------------------

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
    const wchar_t CLASS_NAME[] = L"WahlomatWindowClass";

    WNDCLASSW wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = CreateSolidBrush(COLOR_BG_DARK);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    RegisterClassW(&wc);

    HWND hwnd = CreateWindowExW(
        0, CLASS_NAME, L"Wahl-O-Mat KI - These Bewerter",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 560,
        NULL, NULL, hInstance, NULL);

    if (!hwnd) return 0;

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}