use std::{
    collections::HashSet,
    path::{Path, PathBuf},
    sync::mpsc::{self, Receiver},
    time::Duration,
};

use companytool_shared_protocol::{
    crypto::SessionKey, ClientMessage, HelloMessage, RecordSummary, ServerMessage, WireMessage,
    PROTOCOL_VERSION,
};
use eframe::egui;
use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};

const DEFAULT_CONFIG_PATH: &str = "desktop-client/companytool-client.toml";
const DEFAULT_API_BASE_URL: &str = "http://localhost:8080";
const DEFAULT_WS_URL: &str = "ws://localhost:8080/ws";
const LOGO_BYTES: &[u8] = include_bytes!("../../images/icons/companytool-logo.png");

fn main() -> eframe::Result<()> {
    let args = std::env::args().collect::<Vec<_>>();
    let config =
        ClientConfig::load(resolve_config_path(&args).as_deref()).unwrap_or_else(|error| {
            eprintln!("client config konnte nicht geladen werden: {error}");
            eprintln!("verwende standardkonfiguration");
            ClientConfig::default()
        });
    let api_base_url = resolve_api_base_url(&args, &config);
    let ws_url = resolve_ws_url(&args, &config);

    if args.iter().any(|arg| arg == "--communication-test") {
        match run_headless_communication_test(&ws_url) {
            Ok(()) => {
                println!("native client communication test ok");
                return Ok(());
            }
            Err(error) => {
                eprintln!("native client communication test failed: {error}");
                std::process::exit(1);
            }
        }
    }

    if args.iter().any(|arg| arg == "--registration-test") {
        match run_headless_registration_test(&args, &api_base_url) {
            Ok(()) => {
                println!("native client registration test ok");
                return Ok(());
            }
            Err(error) => {
                eprintln!("native client registration test failed: {error}");
                std::process::exit(1);
            }
        }
    }

    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default()
            .with_inner_size([1280.0, 720.0])
            .with_min_inner_size([1100.0, 650.0])
            .with_icon(eframe::icon_data::from_png_bytes(LOGO_BYTES).unwrap_or_default()),
        ..Default::default()
    };
    eframe::run_native(
        "Company Tool",
        options,
        Box::new(|cc| {
            Ok(Box::new(CompanyToolApp::new(
                &api_base_url,
                &ws_url,
                &cc.egui_ctx,
            )))
        }),
    )
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ClientConfig {
    server: ServerConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ServerConfig {
    #[serde(default = "default_api_base_url")]
    api_base_url: String,
    #[serde(default = "default_ws_url")]
    ws_url: String,
}

impl Default for ClientConfig {
    fn default() -> Self {
        Self {
            server: ServerConfig {
                api_base_url: DEFAULT_API_BASE_URL.to_string(),
                ws_url: DEFAULT_WS_URL.to_string(),
            },
        }
    }
}

fn default_api_base_url() -> String {
    DEFAULT_API_BASE_URL.to_string()
}

fn default_ws_url() -> String {
    DEFAULT_WS_URL.to_string()
}

impl ClientConfig {
    fn load(path: Option<&Path>) -> anyhow::Result<Self> {
        let Some(path) = path else {
            return Ok(Self::default());
        };

        if !path.exists() {
            return Ok(Self::default());
        }

        let content = std::fs::read_to_string(path)?;
        let config = toml::from_str(&content)?;
        Ok(config)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn missing_config_uses_defaults() {
        let config = ClientConfig::load(Some(Path::new("/tmp/companytool-missing-config.toml")))
            .expect("missing config should use defaults");

        assert_eq!(config.server.api_base_url, DEFAULT_API_BASE_URL);
        assert_eq!(config.server.ws_url, DEFAULT_WS_URL);
    }

    #[test]
    fn config_file_overrides_server_urls() {
        let path = std::env::temp_dir().join(format!(
            "companytool-client-test-{}.toml",
            std::process::id()
        ));
        std::fs::write(
            &path,
            r#"
[server]
api_base_url = "http://127.0.0.1:18087"
ws_url = "ws://127.0.0.1:18087/ws"
"#,
        )
        .expect("write test config");

        let config = ClientConfig::load(Some(&path)).expect("config should load");
        let _ = std::fs::remove_file(&path);

        assert_eq!(config.server.api_base_url, "http://127.0.0.1:18087");
        assert_eq!(config.server.ws_url, "ws://127.0.0.1:18087/ws");
    }

    #[test]
    fn command_line_urls_override_config() {
        let config = ClientConfig::default();
        let args = vec![
            "companytool-desktop-client".to_string(),
            "--api-url".to_string(),
            "http://example.test:8081".to_string(),
            "--ws-url".to_string(),
            "ws://example.test:8081/ws".to_string(),
        ];

        assert_eq!(
            resolve_api_base_url(&args, &config),
            "http://example.test:8081"
        );
        assert_eq!(resolve_ws_url(&args, &config), "ws://example.test:8081/ws");
    }
}

fn resolve_config_path(args: &[String]) -> Option<PathBuf> {
    if let Some(path) = args
        .iter()
        .position(|arg| arg == "--config")
        .and_then(|index| args.get(index + 1))
    {
        return Some(PathBuf::from(path));
    }

    if let Ok(path) = std::env::var("COMPANYTOOL_CLIENT_CONFIG") {
        return Some(PathBuf::from(path));
    }

    Some(PathBuf::from(DEFAULT_CONFIG_PATH))
}

fn resolve_ws_url(args: &[String], config: &ClientConfig) -> String {
    args.iter()
        .position(|arg| arg == "--ws-url")
        .and_then(|index| args.get(index + 1))
        .cloned()
        .or_else(|| std::env::var("COMPANYTOOL_WS_URL").ok())
        .unwrap_or_else(|| config.server.ws_url.clone())
}

fn resolve_api_base_url(args: &[String], config: &ClientConfig) -> String {
    args.iter()
        .position(|arg| arg == "--api-url")
        .and_then(|index| args.get(index + 1))
        .cloned()
        .or_else(|| std::env::var("COMPANYTOOL_API_BASE_URL").ok())
        .unwrap_or_else(|| config.server.api_base_url.clone())
}

fn arg_value(args: &[String], name: &str) -> Option<String> {
    args.iter()
        .position(|arg| arg == name)
        .and_then(|index| args.get(index + 1))
        .cloned()
}

fn run_headless_registration_test(args: &[String], api_base_url: &str) -> anyhow::Result<()> {
    let organization_name = arg_value(args, "--organization-name")
        .unwrap_or_else(|| "Native Client Test GmbH".to_string());
    let email = arg_value(args, "--email").unwrap_or_else(|| {
        format!(
            "native-client-test-{}@example.test",
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|duration| duration.as_secs())
                .unwrap_or_default()
        )
    });

    let runtime = tokio::runtime::Runtime::new()?;
    let response = runtime
        .block_on(register_organization(
            api_base_url,
            organization_name,
            email.clone(),
            true,
        ))
        .map_err(anyhow::Error::msg)?;

    anyhow::ensure!(
        !response.id.trim().is_empty(),
        "registrierung lieferte keine id"
    );
    println!(
        "registration id={} status={} email={}",
        response.id, response.status, email
    );
    Ok(())
}

fn run_headless_communication_test(ws_url: &str) -> anyhow::Result<()> {
    let receiver = spawn_socket_client(ws_url);
    let mut saw_snapshot = false;
    let mut saw_pong = false;
    let deadline = std::time::Instant::now() + Duration::from_secs(5);

    while std::time::Instant::now() < deadline {
        match receiver.recv_timeout(Duration::from_millis(250)) {
            Ok(ServerMessage::Snapshot { records }) => {
                anyhow::ensure!(!records.is_empty(), "snapshot hatte keine datensaetze");
                saw_snapshot = true;
            }
            Ok(ServerMessage::Pong) => {
                saw_pong = true;
            }
            Ok(ServerMessage::Error { message }) => {
                anyhow::bail!(message);
            }
            Ok(ServerMessage::RecordChanged { .. }) => {}
            Err(mpsc::RecvTimeoutError::Timeout) => {}
            Err(error) => anyhow::bail!(error),
        }

        if saw_snapshot && saw_pong {
            return Ok(());
        }
    }

    anyhow::bail!("timeout: snapshot={saw_snapshot}, pong={saw_pong}")
}

fn spawn_socket_client(ws_url: &str) -> Receiver<ServerMessage> {
    let (sender, receiver) = mpsc::channel();
    let url = ws_url.to_string();

    std::thread::spawn(move || {
        let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
        runtime.block_on(async move {
            match tokio_tungstenite::connect_async(&url).await {
                Ok((stream, _)) => {
                    let (mut write, mut read) = stream.split();
                    let session_key = SessionKey::generate();
                    let hello = WireMessage::Hello(HelloMessage {
                        protocol_version: PROTOCOL_VERSION,
                        key_id: session_key.key_id().to_string(),
                        session_key: session_key.to_base64(),
                    });

                    if let Ok(text) = serde_json::to_string(&hello) {
                        let _ = write
                            .send(tokio_tungstenite::tungstenite::Message::Text(text))
                            .await;
                    }

                    while let Some(Ok(message)) = read.next().await {
                        if let Ok(text) = message.to_text() {
                            match serde_json::from_str::<WireMessage>(text) {
                                Ok(WireMessage::HelloAck(_)) => {
                                    send_encrypted_client_message(
                                        &mut write,
                                        &session_key,
                                        ClientMessage::Subscribe {
                                            topic: "records".to_string(),
                                        },
                                    )
                                    .await;
                                    send_encrypted_client_message(
                                        &mut write,
                                        &session_key,
                                        ClientMessage::Ping,
                                    )
                                    .await;
                                }
                                Ok(WireMessage::Encrypted(envelope)) => {
                                    if let Ok(server_message) =
                                        session_key.decrypt::<ServerMessage>(&envelope)
                                    {
                                        let _ = sender.send(server_message);
                                    }
                                }
                                Ok(WireMessage::Error(error)) => {
                                    let _ = sender.send(ServerMessage::Error {
                                        message: error.message,
                                    });
                                }
                                Ok(WireMessage::Hello(_)) | Err(_) => {}
                            }
                        }
                    }
                }
                Err(error) => {
                    let _ = sender.send(ServerMessage::Error {
                        message: format!("Verbindung fehlgeschlagen: {error}"),
                    });
                }
            }
        });
    });

    receiver
}

async fn send_encrypted_client_message(
    write: &mut futures_util::stream::SplitSink<
        tokio_tungstenite::WebSocketStream<
            tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
        >,
        tokio_tungstenite::tungstenite::Message,
    >,
    session_key: &SessionKey,
    message: ClientMessage,
) {
    if let Ok(envelope) = session_key.encrypt(&message) {
        if let Ok(text) = serde_json::to_string(&WireMessage::Encrypted(envelope)) {
            let _ = write
                .send(tokio_tungstenite::tungstenite::Message::Text(text))
                .await;
        }
    }
}

fn apply_app_style(ctx: &egui::Context) {
    let mut style = (*ctx.style()).clone();
    style.visuals = egui::Visuals::light();
    style.visuals.window_fill = background_color();
    style.visuals.panel_fill = background_color();
    style.visuals.extreme_bg_color = egui::Color32::from_rgb(255, 255, 255);
    style.visuals.window_stroke = egui::Stroke::new(1.0, egui::Color32::from_rgb(198, 216, 218));
    style.visuals.widgets.noninteractive.bg_fill = egui::Color32::from_rgb(238, 248, 246);
    style.visuals.widgets.noninteractive.fg_stroke =
        egui::Stroke::new(1.0, egui::Color32::from_rgb(23, 32, 38));
    style.visuals.override_text_color = None;
    style.visuals.widgets.inactive.rounding = 6.0.into();
    style.visuals.widgets.inactive.bg_fill = accent_soft_color();
    style.visuals.widgets.inactive.fg_stroke =
        egui::Stroke::new(1.0, egui::Color32::from_rgb(23, 32, 38));
    style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, accent_soft_color());
    style.visuals.widgets.hovered.rounding = 6.0.into();
    style.visuals.widgets.hovered.bg_fill = egui::Color32::from_rgb(199, 235, 230);
    style.visuals.widgets.hovered.fg_stroke =
        egui::Stroke::new(1.0, egui::Color32::from_rgb(23, 32, 38));
    style.visuals.widgets.hovered.bg_stroke = egui::Stroke::new(1.0, accent_color());
    style.visuals.widgets.active.rounding = 6.0.into();
    style.visuals.widgets.active.bg_fill = accent_color();
    style.visuals.widgets.active.fg_stroke = egui::Stroke::new(1.0, egui::Color32::WHITE);
    style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, accent_color());
    ctx.set_style(style);
}

fn background_color() -> egui::Color32 {
    egui::Color32::from_rgb(246, 250, 249)
}

fn surface_color() -> egui::Color32 {
    egui::Color32::from_rgb(255, 255, 255)
}

fn accent_color() -> egui::Color32 {
    egui::Color32::from_rgb(17, 138, 127)
}

fn accent_dark_color() -> egui::Color32 {
    egui::Color32::from_rgb(16, 84, 92)
}

fn accent_soft_color() -> egui::Color32 {
    egui::Color32::from_rgb(222, 244, 240)
}

fn muted_text_color() -> egui::Color32 {
    egui::Color32::from_rgb(90, 106, 114)
}

fn load_logo_texture(ctx: &egui::Context) -> Option<egui::TextureHandle> {
    let image = image::load_from_memory(LOGO_BYTES).ok()?.to_rgba8();
    let size = [image.width() as usize, image.height() as usize];
    let pixels = image.into_raw();
    Some(ctx.load_texture(
        "companytool-logo",
        egui::ColorImage::from_rgba_unmultiplied(size, &pixels),
        egui::TextureOptions::LINEAR,
    ))
}

fn brand(ui: &mut egui::Ui, logo: Option<&egui::TextureHandle>) {
    ui.horizontal(|ui| {
        if let Some(logo) = logo {
            ui.add(egui::Image::new((logo.id(), egui::vec2(34.0, 34.0))).rounding(6.0));
        } else {
            let (rect, _) = ui.allocate_exact_size(egui::vec2(34.0, 34.0), egui::Sense::hover());
            ui.painter().rect_filled(rect, 6.0, accent_color());
        }
        ui.vertical(|ui| {
            ui.label(egui::RichText::new("Company Tool").strong());
            ui.label(
                egui::RichText::new("Organisationen")
                    .color(muted_text_color())
                    .size(13.0),
            );
        });
    });
}

fn nav_group_label(ui: &mut egui::Ui, label: &str) {
    ui.add_space(12.0);
    ui.label(
        egui::RichText::new(label)
            .color(muted_text_color())
            .size(11.0)
            .strong(),
    );
}

fn page_header(ui: &mut egui::Ui, title: &str, description: &str) {
    ui.heading(egui::RichText::new(title).size(28.0).strong());
    ui.add_space(4.0);
    ui.label(egui::RichText::new(description).color(muted_text_color()));
    ui.add_space(18.0);
}

fn panel(ui: &mut egui::Ui, add_contents: impl FnOnce(&mut egui::Ui)) {
    egui::Frame::none()
        .fill(surface_color())
        .stroke(egui::Stroke::new(
            1.0,
            egui::Color32::from_rgb(217, 225, 228),
        ))
        .rounding(8.0)
        .inner_margin(egui::Margin::same(18.0))
        .show(ui, |ui| {
            add_contents(ui);
        });
    ui.add_space(18.0);
}

fn window_minimize_control(ui: &mut egui::Ui) -> bool {
    let mut clicked = false;
    ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
        if ui.add_sized([34.0, 26.0], egui::Button::new("_")).clicked() {
            clicked = true;
        }
    });
    clicked
}

fn sidebar_action_button(label: &str) -> egui::Button<'static> {
    egui::Button::new(
        egui::RichText::new(label.to_string())
            .color(egui::Color32::WHITE)
            .strong(),
    )
    .fill(accent_dark_color())
    .stroke(egui::Stroke::NONE)
    .rounding(6.0)
}

fn primary_action_button(label: &str) -> egui::Button<'static> {
    egui::Button::new(
        egui::RichText::new(label.to_string())
            .color(egui::Color32::WHITE)
            .strong(),
    )
    .fill(accent_color())
    .stroke(egui::Stroke::NONE)
    .rounding(6.0)
}

fn form_row(ui: &mut egui::Ui, label: &str, add_field: impl FnOnce(&mut egui::Ui)) {
    ui.vertical(|ui| {
        ui.label(
            egui::RichText::new(label)
                .color(egui::Color32::from_rgb(67, 82, 88))
                .strong()
                .size(14.0),
        );
        ui.add_space(3.0);
        ui.scope(|ui| {
            ui.set_width(ui.available_width().min(680.0));
            add_field(ui);
        });
    });
    ui.add_space(10.0);
}

macro_rules! master_detail {
    ($ui:expr, $left:block, $right:block $(,)?) => {{
        let _ = $ui;
        $left
        $right
    }};
}

fn cash_discount_combo(
    ui: &mut egui::Ui,
    id: &str,
    selected_id: &mut Option<String>,
    terms: &[CashDiscountTerm],
) {
    let selected_text = selected_id
        .as_ref()
        .and_then(|id| terms.iter().find(|term| &term.id == id))
        .map(|term| format!("{} - {}", term.code, term.name))
        .unwrap_or_else(|| "Keine".to_string());
    egui::ComboBox::from_id_salt(id)
        .selected_text(selected_text)
        .show_ui(ui, |ui| {
            ui.selectable_value(selected_id, None, "Keine");
            for term in terms.iter().filter(|term| term.is_active) {
                ui.selectable_value(
                    selected_id,
                    Some(term.id.clone()),
                    format!("{} - {}", term.code, term.name),
                );
            }
        });
}

fn matches_number_or_name(number: &str, name: &str, query: &str) -> bool {
    let query = query.trim().to_lowercase();
    query.is_empty()
        || number.to_lowercase().contains(&query)
        || name.to_lowercase().contains(&query)
}

fn number_or_pending(number: &str) -> &str {
    if number.trim().is_empty() {
        "wird automatisch vergeben"
    } else {
        number
    }
}

fn unit_combo(ui: &mut egui::Ui, unit: &mut String) {
    let selected_text = if unit.trim().is_empty() {
        "Bitte wählen".to_string()
    } else {
        unit.clone()
    };
    egui::ComboBox::from_id_salt(ui.next_auto_id())
        .selected_text(selected_text)
        .show_ui(ui, |ui| {
            ui.text_edit_singleline(unit);
            ui.separator();
            for option in ["Stck", "kg", "g", "L", "mg", "ml", "qm", "m", "cm", "mm"] {
                ui.selectable_value(unit, option.to_string(), option);
            }
        });
}

fn tax_rate_combo(ui: &mut egui::Ui, tax_rate: &mut String) {
    egui::ComboBox::from_id_salt(ui.next_auto_id())
        .selected_text(format!("{} %", tax_rate.trim()))
        .show_ui(ui, |ui| {
            for option in ["0", "7", "19"] {
                ui.selectable_value(tax_rate, option.to_string(), format!("{option} %"));
            }
        });
}

fn normalize_tax_rate(value: &str) -> String {
    match value.trim().parse::<f64>() {
        Ok(number) if (number - 0.0).abs() < f64::EPSILON => "0".to_string(),
        Ok(number) if (number - 7.0).abs() < f64::EPSILON => "7".to_string(),
        Ok(number) if (number - 19.0).abs() < f64::EPSILON => "19".to_string(),
        _ => value.trim().to_string(),
    }
}

fn format_euro(value: Option<&str>) -> String {
    let Some(value) = value else {
        return "-".to_string();
    };
    let normalized = if value.contains(',') {
        value.replace('.', "").replace(',', ".")
    } else {
        value.to_string()
    };
    match normalized.trim().parse::<f64>() {
        Ok(number) => format!("{} EUR", format_number_de(number, 2)),
        Err(_) => value.to_string(),
    }
}

fn format_number_de(number: f64, decimals: usize) -> String {
    let raw = format!("{number:.decimals$}");
    let (integer, fraction) = raw.split_once('.').unwrap_or((&raw, ""));
    let mut grouped = String::new();
    for (index, character) in integer.chars().rev().enumerate() {
        if index > 0 && index % 3 == 0 {
            grouped.push('.');
        }
        grouped.push(character);
    }
    let integer = grouped.chars().rev().collect::<String>();
    if decimals == 0 {
        integer
    } else {
        format!("{integer},{fraction}")
    }
}

fn customer_combo(
    ui: &mut egui::Ui,
    selected_id: &mut String,
    customers: &[Customer],
    search: &mut String,
) {
    let selected_text = customers
        .iter()
        .find(|customer| customer.id == *selected_id)
        .map(|customer| format!("{} - {}", customer.customer_number, customer.name))
        .unwrap_or_else(|| "Bitte wählen".to_string());
    egui::ComboBox::from_id_salt("quote_customer")
        .selected_text(selected_text)
        .show_ui(ui, |ui| {
            ui.text_edit_singleline(search);
            ui.separator();
            ui.selectable_value(selected_id, String::new(), "Bitte wählen");
            for customer in customers
                .iter()
                .filter(|customer| customer.status == "active")
                .filter(|customer| {
                    matches_number_or_name(&customer.customer_number, &customer.name, search)
                })
                .take(20)
            {
                ui.selectable_value(
                    selected_id,
                    customer.id.clone(),
                    format!("{} - {}", customer.customer_number, customer.name),
                );
            }
        });
}

fn item_combo(ui: &mut egui::Ui, selected_id: &mut String, items: &[Item], search: &mut String) {
    let selected_text = items
        .iter()
        .find(|item| item.id == *selected_id)
        .map(|item| format!("{} - {}", item.item_number, item.name))
        .unwrap_or_else(|| "Bitte wählen".to_string());
    egui::ComboBox::from_id_salt("quote_item")
        .selected_text(selected_text)
        .show_ui(ui, |ui| {
            ui.text_edit_singleline(search);
            ui.separator();
            ui.selectable_value(selected_id, String::new(), "Bitte wählen");
            for item in items
                .iter()
                .filter(|item| item.status == "active")
                .filter(|item| matches_number_or_name(&item.item_number, &item.name, search))
                .take(20)
            {
                ui.selectable_value(
                    selected_id,
                    item.id.clone(),
                    format!("{} - {}", item.item_number, item.name),
                );
            }
        });
}

fn supplier_combo(
    ui: &mut egui::Ui,
    selected_id: &mut String,
    suppliers: &[Supplier],
    search: &mut String,
) {
    let selected_text = suppliers
        .iter()
        .find(|supplier| supplier.id == *selected_id)
        .map(|supplier| format!("{} - {}", supplier.supplier_number, supplier.name))
        .unwrap_or_else(|| "Bitte wählen".to_string());
    egui::ComboBox::from_id_salt("invoice_supplier")
        .selected_text(selected_text)
        .show_ui(ui, |ui| {
            ui.text_edit_singleline(search);
            ui.separator();
            ui.selectable_value(selected_id, String::new(), "Bitte wählen");
            for supplier in suppliers
                .iter()
                .filter(|supplier| supplier.status == "active")
                .filter(|supplier| {
                    matches_number_or_name(&supplier.supplier_number, &supplier.name, search)
                })
                .take(20)
            {
                ui.selectable_value(
                    selected_id,
                    supplier.id.clone(),
                    format!("{} - {}", supplier.supplier_number, supplier.name),
                );
            }
        });
}

fn apply_quote_item_defaults(line: &mut QuoteItemForm, items: &[Item]) {
    if let Some(item) = items.iter().find(|item| item.id == line.item_id) {
        line.description = item.name.clone();
        line.unit_price = item
            .default_sales_price
            .clone()
            .unwrap_or_else(|| "0".to_string());
        line.original_unit_price = item.default_sales_price.clone();
        line.tax_rate = normalize_tax_rate(&item.tax_rate);
    }
}

fn apply_quote_customer_defaults(form: &mut QuoteForm, customers: &[Customer]) {
    if let Some(customer) = customers.iter().find(|customer| customer.id == form.customer_id) {
        form.customer_discount_percent = customer.standard_discount_percent.clone();
        form.cash_discount_term_id = customer.cash_discount_term_id.clone();
    }
}

fn apply_outgoing_invoice_customer_defaults(form: &mut OutgoingInvoiceForm, customers: &[Customer]) {
    if let Some(customer) = customers.iter().find(|customer| customer.id == form.customer_id) {
        form.customer_discount_percent = customer.standard_discount_percent.clone();
        form.cash_discount_term_id = customer.cash_discount_term_id.clone();
    }
}

fn invoice_items_editor(
    ui: &mut egui::Ui,
    lines: &mut Vec<OutgoingInvoiceItemForm>,
    items: &[Item],
    item_search: &mut String,
) {
    ui.add_space(8.0);
    ui.horizontal(|ui| {
        ui.heading("Positionen");
        if ui.button("Position hinzufügen").clicked() {
            lines.push(OutgoingInvoiceItemForm::default());
        }
    });
    let mut remove_index = None;
    let can_remove = lines.len() > 1;
    for (index, line) in lines.iter_mut().enumerate() {
        ui.separator();
        ui.label(format!("Position {}", index + 1));
        form_row(ui, "Artikel", |ui| {
            item_combo(ui, &mut line.item_id, items, item_search)
        });
        if ui.button("Artikelwerte übernehmen").clicked() {
            if let Some(item) = items.iter().find(|item| item.id == line.item_id) {
                line.description = item.name.clone();
                line.unit_price = item
                    .default_sales_price
                    .clone()
                    .unwrap_or_else(|| "0".to_string());
                line.original_unit_price = item.default_sales_price.clone();
                line.tax_rate = normalize_tax_rate(&item.tax_rate);
            }
        }
        form_row(ui, "Beschreibung", |ui| {
            ui.text_edit_singleline(&mut line.description);
        });
        egui::Grid::new(format!("outgoing_invoice_item_fields_{index}"))
            .num_columns(2)
            .spacing([14.0, 8.0])
            .show(ui, |ui| {
                ui.label("Menge");
                ui.text_edit_singleline(&mut line.quantity);
                ui.end_row();
                ui.label("Preis");
                ui.text_edit_singleline(&mut line.unit_price);
                ui.end_row();
                ui.label("Rabatt %");
                ui.text_edit_singleline(&mut line.discount_percent);
                ui.end_row();
                ui.label("Steuer %");
                tax_rate_combo(ui, &mut line.tax_rate);
                ui.end_row();
            });
        if can_remove && ui.button("Entfernen").clicked() {
            remove_index = Some(index);
        }
    }
    if let Some(index) = remove_index {
        lines.remove(index);
    }
}

fn incoming_invoice_items_editor(
    ui: &mut egui::Ui,
    lines: &mut Vec<IncomingInvoiceItemForm>,
    items: &[Item],
    item_search: &mut String,
) {
    ui.add_space(8.0);
    ui.horizontal(|ui| {
        ui.heading("Positionen");
        if ui.button("Position hinzufügen").clicked() {
            lines.push(IncomingInvoiceItemForm::default());
        }
    });
    let mut remove_index = None;
    let can_remove = lines.len() > 1;
    for (index, line) in lines.iter_mut().enumerate() {
        ui.separator();
        ui.label(format!("Position {}", index + 1));
        let mut selected = line.item_id.clone().unwrap_or_default();
        form_row(ui, "Artikel", |ui| {
            item_combo(ui, &mut selected, items, item_search)
        });
        line.item_id = if selected.is_empty() {
            None
        } else {
            Some(selected)
        };
        form_row(ui, "Beschreibung", |ui| {
            ui.text_edit_singleline(&mut line.description);
        });
        egui::Grid::new(format!("incoming_invoice_item_fields_{index}"))
            .num_columns(2)
            .spacing([14.0, 8.0])
            .show(ui, |ui| {
                ui.label("Menge");
                ui.text_edit_singleline(&mut line.quantity);
                ui.end_row();
                ui.label("Preis");
                ui.text_edit_singleline(&mut line.unit_price);
                ui.end_row();
                ui.label("Steuer %");
                tax_rate_combo(ui, &mut line.tax_rate);
                ui.end_row();
            });
        if can_remove && ui.button("Entfernen").clicked() {
            remove_index = Some(index);
        }
    }
    if let Some(index) = remove_index {
        lines.remove(index);
    }
}

fn number_range_label(code: &str) -> &'static str {
    match code {
        "items" => "Artikel",
        "activities" => "Aktivitäten",
        "outgoing_invoices" => "Rechnungen",
        "incoming_invoices" => "Eingangsrechnungen",
        "customers" => "Kunden",
        "suppliers" => "Lieferanten",
        "quotes" => "Angebote",
        _ => "Nummernkreis",
    }
}

fn role_checkboxes(ui: &mut egui::Ui, roles: &mut Vec<String>) {
    ui.vertical(|ui| {
        for (code, label) in available_roles() {
            let mut enabled = roles.iter().any(|role| role == code);
            if ui.checkbox(&mut enabled, label).changed() {
                if enabled {
                    roles.push(code.to_string());
                } else {
                    roles.retain(|role| role != code);
                }
            }
        }
    });
}

fn available_roles() -> [(&'static str, &'static str); 5] {
    [
        ("owner", "Besitzer"),
        ("admin", "Admin"),
        ("sales", "Vertrieb"),
        ("accounting", "Buchhaltung"),
        ("viewer", "Lesen"),
    ]
}

fn normalized_roles(roles: &[String]) -> Vec<String> {
    let mut normalized = roles
        .iter()
        .filter(|role| available_roles().iter().any(|(code, _)| code == role))
        .cloned()
        .collect::<Vec<_>>();
    normalized.sort();
    normalized.dedup();
    if normalized.is_empty() {
        normalized.push("viewer".to_string());
    }
    normalized
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum NavigationMode {
    Scroll,
    Groups,
}

impl Default for NavigationMode {
    fn default() -> Self {
        Self::Scroll
    }
}

impl NavigationMode {
    fn label(self) -> &'static str {
        match self {
            Self::Scroll => "Scrollbar",
            Self::Groups => "Gruppen einklappen",
        }
    }

    fn button_height(self) -> f32 {
        36.0
    }
}

struct CompanyToolApp {
    api_base_url: String,
    ws_url: String,
    receiver: Receiver<ServerMessage>,
    logo_texture: Option<egui::TextureHandle>,
    login_sender: mpsc::Sender<LoginResult>,
    login_receiver: Receiver<LoginResult>,
    registration_sender: mpsc::Sender<RegistrationResult>,
    registration_receiver: Receiver<RegistrationResult>,
    dev_bootstrap_sender: mpsc::Sender<DevBootstrapResult>,
    dev_bootstrap_receiver: Receiver<DevBootstrapResult>,
    user_admin_sender: mpsc::Sender<UserAdminEvent>,
    user_admin_receiver: Receiver<UserAdminEvent>,
    admin_sender: mpsc::Sender<AdminEvent>,
    admin_receiver: Receiver<AdminEvent>,
    records: Vec<RecordSummary>,
    status: String,
    live_revision: u64,
    live_update_status: String,
    navigation_mode: NavigationMode,
    navigation_settings_status: String,
    minimized_windows: HashSet<&'static str>,
    view: AppView,
    authenticated_email: Option<String>,
    access_token: Option<String>,
    selected_organization_id: Option<String>,
    login_email: String,
    login_password: String,
    login_status: String,
    login_pending: bool,
    dev_organization_name: String,
    dev_email: String,
    dev_status: String,
    dev_pending: bool,
    dev_credentials: Option<DevBootstrapResponse>,
    organization_name: String,
    registration_email: String,
    accept_terms: bool,
    registration_status: String,
    registration_pending: bool,
    users: Vec<OrganizationUser>,
    users_status: String,
    users_pending: bool,
    saving_user_id: Option<String>,
    invite_email: String,
    invite_roles: Vec<String>,
    users_window_open: bool,
    organization_setup: OrganizationSetupForm,
    organization_setup_status: String,
    organization_setup_pending: bool,
    organization_setup_window_open: bool,
    number_ranges: Vec<NumberRange>,
    number_range_form: NumberRangeForm,
    selected_number_range_code: Option<String>,
    number_ranges_status: String,
    number_ranges_window_open: bool,
    registrations: Vec<OrganizationRegistration>,
    registrations_status: String,
    registrations_pending: bool,
    admin_registrations_window_open: bool,
    customers: Vec<Customer>,
    customer_form: CustomerForm,
    selected_customer_id: Option<String>,
    customers_status: String,
    customers_pending: bool,
    customers_window_open: bool,
    suppliers: Vec<Supplier>,
    supplier_form: SupplierForm,
    selected_supplier_id: Option<String>,
    suppliers_status: String,
    suppliers_window_open: bool,
    cash_discount_terms: Vec<CashDiscountTerm>,
    cash_discount_term_form: CashDiscountTermForm,
    selected_cash_discount_term_id: Option<String>,
    cash_discount_terms_status: String,
    cash_discount_terms_window_open: bool,
    items: Vec<Item>,
    item_form: ItemForm,
    selected_item_id: Option<String>,
    item_price_history: Vec<ItemPriceHistory>,
    items_status: String,
    items_window_open: bool,
    quotes: Vec<Quote>,
    quote_form: QuoteForm,
    selected_quote_id: Option<String>,
    quotes_status: String,
    quotes_window_open: bool,
    outgoing_invoices: Vec<OutgoingInvoice>,
    outgoing_invoice_form: OutgoingInvoiceForm,
    selected_outgoing_invoice_id: Option<String>,
    outgoing_invoices_status: String,
    outgoing_invoices_window_open: bool,
    incoming_invoices: Vec<IncomingInvoice>,
    incoming_invoice_form: IncomingInvoiceForm,
    selected_incoming_invoice_id: Option<String>,
    incoming_invoices_status: String,
    incoming_invoices_window_open: bool,
    activities: Vec<Activity>,
    activity_form: ActivityForm,
    selected_activity_id: Option<String>,
    activities_status: String,
    activities_window_open: bool,
    price_import_form: PriceListImportForm,
    price_import_preview: Option<PriceListImportPreview>,
    price_import_status: String,
    price_import_window_open: bool,
    api_connectors: Vec<ApiConnector>,
    api_connector_form: ApiConnectorForm,
    selected_api_connector_id: Option<String>,
    api_connectors_status: String,
    api_connectors_window_open: bool,
    price_rules: Vec<PriceRule>,
    price_rule_form: PriceRuleForm,
    selected_price_rule_id: Option<String>,
    price_rules_status: String,
    price_rules_window_open: bool,
    customer_lookup_search: String,
    supplier_lookup_search: String,
    item_lookup_search: String,
    customer_list_search: String,
    supplier_list_search: String,
    item_list_search: String,
    item_sort_mode: String,
    activity_list_search: String,
    quote_list_search: String,
    outgoing_invoice_list_search: String,
    incoming_invoice_list_search: String,
}

impl CompanyToolApp {
    fn new(api_base_url: &str, ws_url: &str, ctx: &egui::Context) -> Self {
        let (login_sender, login_receiver) = mpsc::channel();
        let (registration_sender, registration_receiver) = mpsc::channel();
        let (dev_bootstrap_sender, dev_bootstrap_receiver) = mpsc::channel();
        let (user_admin_sender, user_admin_receiver) = mpsc::channel();
        let (admin_sender, admin_receiver) = mpsc::channel();

        Self {
            api_base_url: api_base_url.to_string(),
            ws_url: ws_url.to_string(),
            receiver: spawn_socket_client(ws_url),
            logo_texture: load_logo_texture(ctx),
            login_sender,
            login_receiver,
            registration_sender,
            registration_receiver,
            dev_bootstrap_sender,
            dev_bootstrap_receiver,
            user_admin_sender,
            user_admin_receiver,
            admin_sender,
            admin_receiver,
            records: Vec::new(),
            status: "Verbinde...".to_string(),
            live_revision: 0,
            live_update_status: "Keine Live-Änderung empfangen.".to_string(),
            navigation_mode: NavigationMode::Scroll,
            navigation_settings_status: String::new(),
            minimized_windows: HashSet::new(),
            view: AppView::Login,
            authenticated_email: None,
            access_token: None,
            selected_organization_id: None,
            login_email: String::new(),
            login_password: String::new(),
            login_status: String::new(),
            login_pending: false,
            dev_organization_name: "Lokale Testfirma".to_string(),
            dev_email: "admin@example.test".to_string(),
            dev_status: String::new(),
            dev_pending: false,
            dev_credentials: None,
            organization_name: String::new(),
            registration_email: String::new(),
            accept_terms: false,
            registration_status: String::new(),
            registration_pending: false,
            users: Vec::new(),
            users_status: "Noch keine Benutzer geladen.".to_string(),
            users_pending: false,
            saving_user_id: None,
            invite_email: String::new(),
            invite_roles: vec!["viewer".to_string()],
            users_window_open: false,
            organization_setup: OrganizationSetupForm::default(),
            organization_setup_status: String::new(),
            organization_setup_pending: false,
            organization_setup_window_open: false,
            number_ranges: Vec::new(),
            number_range_form: NumberRangeForm::default(),
            selected_number_range_code: None,
            number_ranges_status: "Noch keine Nummernkreise geladen.".to_string(),
            number_ranges_window_open: false,
            registrations: Vec::new(),
            registrations_status: "Noch keine Registrierungen geladen.".to_string(),
            registrations_pending: false,
            admin_registrations_window_open: false,
            customers: Vec::new(),
            customer_form: CustomerForm::default(),
            selected_customer_id: None,
            customers_status: "Noch keine Kunden geladen.".to_string(),
            customers_pending: false,
            customers_window_open: false,
            suppliers: Vec::new(),
            supplier_form: SupplierForm::default(),
            selected_supplier_id: None,
            suppliers_status: "Noch keine Lieferanten geladen.".to_string(),
            suppliers_window_open: false,
            cash_discount_terms: Vec::new(),
            cash_discount_term_form: CashDiscountTermForm::default(),
            selected_cash_discount_term_id: None,
            cash_discount_terms_status: "Noch keine Skonto-Regeln geladen.".to_string(),
            cash_discount_terms_window_open: false,
            items: Vec::new(),
            item_form: ItemForm::default(),
            selected_item_id: None,
            item_price_history: Vec::new(),
            items_status: "Noch keine Artikel geladen.".to_string(),
            items_window_open: false,
            quotes: Vec::new(),
            quote_form: QuoteForm::default(),
            selected_quote_id: None,
            quotes_status: "Noch keine Angebote geladen.".to_string(),
            quotes_window_open: false,
            outgoing_invoices: Vec::new(),
            outgoing_invoice_form: OutgoingInvoiceForm::default(),
            selected_outgoing_invoice_id: None,
            outgoing_invoices_status: "Noch keine Ausgangsrechnungen geladen.".to_string(),
            outgoing_invoices_window_open: false,
            incoming_invoices: Vec::new(),
            incoming_invoice_form: IncomingInvoiceForm::default(),
            selected_incoming_invoice_id: None,
            incoming_invoices_status: "Noch keine Eingangsrechnungen geladen.".to_string(),
            incoming_invoices_window_open: false,
            activities: Vec::new(),
            activity_form: ActivityForm::default(),
            selected_activity_id: None,
            activities_status: "Noch keine Aktivitäten geladen.".to_string(),
            activities_window_open: false,
            price_import_form: PriceListImportForm::default(),
            price_import_preview: None,
            price_import_status: "Noch keine Preisliste geprüft.".to_string(),
            price_import_window_open: false,
            api_connectors: Vec::new(),
            api_connector_form: ApiConnectorForm::default(),
            selected_api_connector_id: None,
            api_connectors_status: "Noch keine Preis-APIs geladen.".to_string(),
            api_connectors_window_open: false,
            price_rules: Vec::new(),
            price_rule_form: PriceRuleForm::default(),
            selected_price_rule_id: None,
            price_rules_status: "Noch keine Preisregeln geladen.".to_string(),
            price_rules_window_open: false,
            customer_lookup_search: String::new(),
            supplier_lookup_search: String::new(),
            item_lookup_search: String::new(),
            customer_list_search: String::new(),
            supplier_list_search: String::new(),
            item_list_search: String::new(),
            item_sort_mode: "name_desc".to_string(),
            activity_list_search: String::new(),
            quote_list_search: String::new(),
            outgoing_invoice_list_search: String::new(),
            incoming_invoice_list_search: String::new(),
        }
    }

    fn drain_messages(&mut self) {
        while let Ok(message) = self.receiver.try_recv() {
            match message {
                ServerMessage::Snapshot { records } => {
                    self.records = records;
                    self.status = "Verbunden".to_string();
                }
                ServerMessage::RecordChanged { record } => {
                    self.live_revision += 1;
                    self.live_update_status =
                        format!("Live-Änderung {}: {}", self.live_revision, record.title);
                    if let Some(existing) =
                        self.records.iter_mut().find(|item| item.id == record.id)
                    {
                        *existing = record;
                    } else {
                        self.records.insert(0, record);
                    }
                    self.status = "Aktualisiert".to_string();
                    if self.customers_window_open {
                        self.load_customers();
                    }
                    if self.suppliers_window_open {
                        self.load_suppliers();
                    }
                    if self.cash_discount_terms_window_open
                        || self.customers_window_open
                        || self.suppliers_window_open
                    {
                        self.load_cash_discount_terms();
                    }
                    if self.items_window_open {
                        self.load_items();
                        if let Some(item_id) = self.selected_item_id.clone() {
                            self.load_item_price_history(item_id);
                        }
                    }
                    if self.quotes_window_open {
                        self.load_quotes();
                        self.load_customers();
                        self.load_items();
                    }
                    if self.outgoing_invoices_window_open {
                        self.load_outgoing_invoices();
                        self.load_customers();
                        self.load_items();
                    }
                    if self.incoming_invoices_window_open {
                        self.load_incoming_invoices();
                        self.load_suppliers();
                        self.load_items();
                    }
                    if self.activities_window_open {
                        self.load_activities();
                    }
                    if self.api_connectors_window_open {
                        self.load_api_connectors();
                    }
                    if self.price_rules_window_open {
                        self.load_price_rules();
                    }
                    if self.price_import_window_open && self.price_import_preview.is_some() {
                        self.preview_price_import();
                    }
                }
                ServerMessage::Pong => {
                    self.status = "Backend erreichbar".to_string();
                }
                ServerMessage::Error { message } => {
                    self.status = message;
                }
            }
        }

        while let Ok(result) = self.login_receiver.try_recv() {
            self.login_pending = false;
            match result {
                Ok(response) => {
                    self.authenticated_email = Some(self.login_email.trim().to_string());
                    self.access_token = Some(response.access_token.clone());
                    self.selected_organization_id =
                        response.organization_id.clone().or_else(|| {
                            response
                                .organizations
                                .first()
                                .map(|organization| organization.id.clone())
                        });
                    self.login_password.clear();
                    self.load_user_navigation_settings();
                    let organization_text = match response.organizations.first() {
                        Some(organization) => format!(
                            "{} Organization(en), aktiv: {} ({})",
                            response.organizations.len(),
                            organization
                                .schema_name
                                .as_deref()
                                .unwrap_or(&organization.id),
                            organization.status
                        ),
                        None => "keine aktive Organization".to_string(),
                    };
                    self.login_status = if response.must_change_password {
                        format!(
                            "Login erfolgreich für {}. Passwortänderung ist erforderlich. {}.",
                            response.user_id, organization_text
                        )
                    } else {
                        format!(
                            "Login erfolgreich für {}. {}.",
                            response.user_id, organization_text
                        )
                    };
                    self.view = AppView::Status;
                }
                Err(message) => {
                    self.login_status = message;
                }
            }
        }

        while let Ok(result) = self.registration_receiver.try_recv() {
            self.registration_pending = false;
            self.registration_status = match result {
                Ok(response) => format!(
                    "Registrierung eingegangen. Status: {}, ID: {}",
                    response.status, response.id
                ),
                Err(message) => message,
            };
        }

        while let Ok(result) = self.dev_bootstrap_receiver.try_recv() {
            self.dev_pending = false;
            match result {
                Ok(response) => {
                    self.login_email = response.email.clone();
                    self.login_password = response.password.clone();
                    self.dev_status = "Testfirma und User wurden angelegt.".to_string();
                    self.dev_credentials = Some(response);
                }
                Err(message) => {
                    self.dev_status = message;
                    self.dev_credentials = None;
                }
            }
        }

        while let Ok(event) = self.user_admin_receiver.try_recv() {
            match event {
                UserAdminEvent::UsersLoaded(result) => {
                    self.users_pending = false;
                    match result {
                        Ok(users) => {
                            self.users = users;
                            self.users_status = if self.users.is_empty() {
                                "Keine Benutzer vorhanden.".to_string()
                            } else {
                                "Benutzer geladen.".to_string()
                            };
                        }
                        Err(message) => {
                            self.users.clear();
                            self.users_status = message;
                        }
                    }
                }
                UserAdminEvent::InvitationSent(result) => {
                    self.users_pending = false;
                    match result {
                        Ok(message) => {
                            self.users_status = message;
                            self.invite_email.clear();
                            self.load_users();
                        }
                        Err(message) => {
                            self.users_status = message;
                        }
                    }
                }
                UserAdminEvent::RolesSaved { user_id, result } => {
                    self.saving_user_id = None;
                    match result {
                        Ok(()) => {
                            self.users_status = "Benutzerrechte gespeichert.".to_string();
                            self.load_users();
                        }
                        Err(message) => {
                            self.users_status = format!("{user_id}: {message}");
                        }
                    }
                }
            }
        }

        while let Ok(event) = self.admin_receiver.try_recv() {
            match event {
                AdminEvent::NavigationSettingsLoaded(result) => match result {
                    Ok(settings) => {
                        self.navigation_mode = settings.mode;
                        self.navigation_settings_status = String::new();
                    }
                    Err(message) => {
                        self.navigation_settings_status = message;
                    }
                },
                AdminEvent::NavigationSettingsSaved(result) => match result {
                    Ok(settings) => {
                        self.navigation_mode = settings.mode;
                        self.navigation_settings_status = "Menü gespeichert.".to_string();
                    }
                    Err(message) => {
                        self.navigation_settings_status = message;
                    }
                },
                AdminEvent::OrganizationSetupSaved(result) => {
                    self.organization_setup_pending = false;
                    self.organization_setup_status = match result {
                        Ok(()) => "Firmendaten gespeichert.".to_string(),
                        Err(message) => message,
                    };
                }
                AdminEvent::OrganizationSetupLoaded(result) => {
                    self.organization_setup_pending = false;
                    match result {
                        Ok(Some(setup)) => {
                            self.organization_setup = setup;
                            self.organization_setup_status = "Firmendaten geladen.".to_string();
                        }
                        Ok(None) => {
                            self.organization_setup_status =
                                "Noch keine Firmendaten gespeichert.".to_string();
                        }
                        Err(message) => {
                            self.organization_setup_status = message;
                        }
                    }
                }
                AdminEvent::RegistrationsLoaded(result) => {
                    self.registrations_pending = false;
                    match result {
                        Ok(registrations) => {
                            self.registrations = registrations;
                            self.registrations_status = if self.registrations.is_empty() {
                                "Keine Registrierungen vorhanden.".to_string()
                            } else {
                                "Registrierungen geladen.".to_string()
                            };
                        }
                        Err(message) => {
                            self.registrations.clear();
                            self.registrations_status = message;
                        }
                    }
                }
                AdminEvent::RegistrationActionDone(result) => {
                    self.registrations_pending = false;
                    self.registrations_status = match result {
                        Ok(message) => message,
                        Err(message) => message,
                    };
                    self.load_registrations();
                }
                AdminEvent::NumberRangesLoaded(result) => match result {
                    Ok(records) => {
                        self.number_ranges = records;
                        self.number_ranges_status = "Nummernkreise geladen.".to_string();
                    }
                    Err(message) => self.number_ranges_status = message,
                },
                AdminEvent::NumberRangeSaved(result) => match result {
                    Ok(record) => {
                        self.selected_number_range_code = Some(record.code.clone());
                        self.number_range_form = NumberRangeForm::from(&record);
                        self.number_ranges_status = "Nummernkreis gespeichert.".to_string();
                        self.load_number_ranges();
                    }
                    Err(message) => self.number_ranges_status = message,
                },
                AdminEvent::NextNumberReserved { code, result } => match result {
                    Ok(number) => self.apply_reserved_number(&code, number),
                    Err(message) => self.set_number_status(&code, message),
                },
                AdminEvent::CustomersLoaded(result) => {
                    self.customers_pending = false;
                    match result {
                        Ok(customers) => {
                            self.customers = customers;
                            self.customers_status = "Kunden geladen.".to_string();
                        }
                        Err(message) => {
                            self.customers_status = message;
                        }
                    }
                }
                AdminEvent::CustomerSaved(result) => {
                    self.customers_pending = false;
                    match result {
                        Ok(customer) => {
                            self.selected_customer_id = Some(customer.id.clone());
                            self.customer_form = CustomerForm::from(&customer);
                            self.customers_status = "Kunde gespeichert.".to_string();
                            self.load_customers();
                        }
                        Err(message) => {
                            self.customers_status = message;
                        }
                    }
                }
                AdminEvent::CustomerDeleted(result) => {
                    self.customers_pending = false;
                    self.customers_status = match result {
                        Ok(()) => "Kunde deaktiviert.".to_string(),
                        Err(message) => message,
                    };
                    self.load_customers();
                }
                AdminEvent::SuppliersLoaded(result) => match result {
                    Ok(records) => {
                        self.suppliers = records;
                        self.suppliers_status = "Lieferanten geladen.".to_string();
                    }
                    Err(message) => self.suppliers_status = message,
                },
                AdminEvent::SupplierSaved(result) => match result {
                    Ok(record) => {
                        self.selected_supplier_id = Some(record.id.clone());
                        self.supplier_form = SupplierForm::from(&record);
                        self.suppliers_status = "Lieferant gespeichert.".to_string();
                        self.load_suppliers();
                    }
                    Err(message) => self.suppliers_status = message,
                },
                AdminEvent::SupplierDeleted(result) => {
                    self.suppliers_status = result
                        .map(|_| "Lieferant deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_suppliers();
                }
                AdminEvent::CashDiscountTermsLoaded(result) => match result {
                    Ok(records) => {
                        self.cash_discount_terms = records;
                        self.cash_discount_terms_status = "Skonto-Regeln geladen.".to_string();
                    }
                    Err(message) => self.cash_discount_terms_status = message,
                },
                AdminEvent::CashDiscountTermSaved(result) => match result {
                    Ok(record) => {
                        self.selected_cash_discount_term_id = Some(record.id.clone());
                        self.cash_discount_term_form = CashDiscountTermForm::from(&record);
                        self.cash_discount_terms_status = "Skonto-Regel gespeichert.".to_string();
                        self.load_cash_discount_terms();
                    }
                    Err(message) => self.cash_discount_terms_status = message,
                },
                AdminEvent::CashDiscountTermDeleted(result) => {
                    self.cash_discount_terms_status = result
                        .map(|_| "Skonto-Regel deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_cash_discount_terms();
                }
                AdminEvent::ItemsLoaded(result) => match result {
                    Ok(records) => {
                        self.items = records;
                        self.items_status = "Artikel geladen.".to_string();
                    }
                    Err(message) => self.items_status = message,
                },
                AdminEvent::ItemSaved(result) => match result {
                    Ok(record) => {
                        self.selected_item_id = Some(record.id.clone());
                        self.item_form = ItemForm::from(&record);
                        self.items_status = "Artikel gespeichert.".to_string();
                        self.load_items();
                        self.load_item_price_history(record.id);
                    }
                    Err(message) => self.items_status = message,
                },
                AdminEvent::ItemPriceHistoryLoaded(result) => match result {
                    Ok(records) => self.item_price_history = records,
                    Err(message) => self.items_status = message,
                },
                AdminEvent::ItemDeleted(result) => {
                    self.items_status = result
                        .map(|_| "Artikel deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_items();
                }
                AdminEvent::QuotesLoaded(result) => match result {
                    Ok(records) => {
                        self.quotes = records;
                        self.quotes_status = "Angebote geladen.".to_string();
                    }
                    Err(message) => self.quotes_status = message,
                },
                AdminEvent::QuoteSaved(result) => match result {
                    Ok(record) => {
                        self.selected_quote_id = Some(record.id.clone());
                        self.quote_form = QuoteForm::from(&record);
                        self.quotes_status = "Angebot gespeichert.".to_string();
                        self.load_quotes();
                    }
                    Err(message) => self.quotes_status = message,
                },
                AdminEvent::QuoteDeleted(result) => {
                    self.quotes_status = result
                        .map(|_| "Angebot storniert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_quotes();
                }
                AdminEvent::OutgoingInvoicesLoaded(result) => match result {
                    Ok(records) => {
                        self.outgoing_invoices = records;
                        self.outgoing_invoices_status = "Ausgangsrechnungen geladen.".to_string();
                    }
                    Err(message) => self.outgoing_invoices_status = message,
                },
                AdminEvent::OutgoingInvoiceSaved(result) => match result {
                    Ok(record) => {
                        self.selected_outgoing_invoice_id = Some(record.id.clone());
                        self.outgoing_invoice_form = OutgoingInvoiceForm::from(&record);
                        self.outgoing_invoices_status = "Ausgangsrechnung gespeichert.".to_string();
                        self.load_outgoing_invoices();
                    }
                    Err(message) => self.outgoing_invoices_status = message,
                },
                AdminEvent::OutgoingInvoiceDeleted(result) => {
                    self.outgoing_invoices_status = result
                        .map(|_| "Ausgangsrechnung storniert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_outgoing_invoices();
                }
                AdminEvent::OutgoingInvoiceFinalized(result) => {
                    self.outgoing_invoices_status = result
                        .map(|_| "Ausgangsrechnung abgeschlossen.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_outgoing_invoices();
                }
                AdminEvent::IncomingInvoicesLoaded(result) => match result {
                    Ok(records) => {
                        self.incoming_invoices = records;
                        self.incoming_invoices_status = "Eingangsrechnungen geladen.".to_string();
                    }
                    Err(message) => self.incoming_invoices_status = message,
                },
                AdminEvent::IncomingInvoiceSaved(result) => match result {
                    Ok(record) => {
                        self.selected_incoming_invoice_id = Some(record.id.clone());
                        self.incoming_invoice_form = IncomingInvoiceForm::from(&record);
                        self.incoming_invoices_status = "Eingangsrechnung gespeichert.".to_string();
                        self.load_incoming_invoices();
                    }
                    Err(message) => self.incoming_invoices_status = message,
                },
                AdminEvent::IncomingInvoiceDeleted(result) => {
                    self.incoming_invoices_status = result
                        .map(|_| "Eingangsrechnung storniert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_incoming_invoices();
                }
                AdminEvent::ActivitiesLoaded(result) => match result {
                    Ok(records) => {
                        self.activities = records;
                        self.activities_status = "Aktivitäten geladen.".to_string();
                    }
                    Err(message) => self.activities_status = message,
                },
                AdminEvent::ActivitySaved(result) => match result {
                    Ok(record) => {
                        self.selected_activity_id = Some(record.id.clone());
                        self.activity_form = ActivityForm::from(&record);
                        self.activities_status = "Aktivität gespeichert.".to_string();
                        self.load_activities();
                    }
                    Err(message) => self.activities_status = message,
                },
                AdminEvent::ActivityDeleted(result) => {
                    self.activities_status = result
                        .map(|_| "Aktivität deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_activities();
                }
                AdminEvent::PriceImportPreviewed(result) => match result {
                    Ok(preview) => {
                        self.price_import_status = format!(
                            "Vorschau: {} gültig, {} Fehler.",
                            preview.valid_rows, preview.error_rows
                        );
                        self.price_import_preview = Some(preview);
                    }
                    Err(message) => self.price_import_status = message,
                },
                AdminEvent::PriceImportApplied(result) => match result {
                    Ok(response) => {
                        self.price_import_status = format!(
                            "Import {}: {} Zeilen übernommen, {} Fehler.",
                            response.import_id, response.applied_rows, response.error_rows
                        );
                        self.load_items();
                        self.preview_price_import();
                    }
                    Err(message) => self.price_import_status = message,
                },
                AdminEvent::ApiConnectorsLoaded(result) => match result {
                    Ok(records) => {
                        self.api_connectors = records;
                        self.api_connectors_status = "Preis-APIs geladen.".to_string();
                    }
                    Err(message) => self.api_connectors_status = message,
                },
                AdminEvent::ApiConnectorSaved(result) => match result {
                    Ok(record) => {
                        self.selected_api_connector_id = Some(record.id.clone());
                        self.api_connector_form = ApiConnectorForm::from(&record);
                        self.api_connectors_status = "Preis-API gespeichert.".to_string();
                        self.load_api_connectors();
                    }
                    Err(message) => self.api_connectors_status = message,
                },
                AdminEvent::ApiConnectorDeleted(result) => {
                    self.api_connectors_status = result
                        .map(|_| "Preis-API deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_api_connectors();
                }
                AdminEvent::ApiConnectorSynced(result) => match result {
                    Ok(response) => {
                        self.api_connectors_status = format!(
                            "Abgleich {} ausgeführt: {} Zeilen übernommen, {} Fehler.",
                            &response.id[..response.id.len().min(8)],
                            response.applied_rows,
                            response.error_rows
                        );
                        if !response.synced {
                            self.api_connectors_status =
                                "Abgleich wurde nicht bestätigt.".to_string();
                        }
                        self.load_api_connectors();
                        self.load_items();
                    }
                    Err(message) => self.api_connectors_status = message,
                },
                AdminEvent::PriceRulesLoaded(result) => match result {
                    Ok(records) => {
                        self.price_rules = records;
                        self.price_rules_status = "Preisregeln geladen.".to_string();
                    }
                    Err(message) => self.price_rules_status = message,
                },
                AdminEvent::PriceRuleSaved(result) => match result {
                    Ok(record) => {
                        self.selected_price_rule_id = Some(record.id.clone());
                        self.price_rule_form = PriceRuleForm::from(&record);
                        self.price_rules_status = "Preisregel gespeichert.".to_string();
                        self.load_price_rules();
                    }
                    Err(message) => self.price_rules_status = message,
                },
                AdminEvent::PriceRuleDeleted(result) => {
                    self.price_rules_status = result
                        .map(|_| "Preisregel deaktiviert.".to_string())
                        .unwrap_or_else(|message| message);
                    self.load_price_rules();
                }
            }
        }
    }

    fn submit_login(&mut self) {
        let email = self.login_email.trim().to_string();
        let password = self.login_password.clone();

        if !email.contains('@') {
            self.login_status = "E-Mail-Adresse ist ungültig.".to_string();
            return;
        }

        if password.is_empty() {
            self.login_status = "Passwort fehlt.".to_string();
            return;
        }

        self.login_pending = true;
        self.login_status = "Melde an...".to_string();

        let api_base_url = self.api_base_url.clone();
        let sender = self.login_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = login_user(&api_base_url, email, password).await;
                let _ = sender.send(result);
            });
        });
    }

    fn logout(&mut self) {
        self.authenticated_email = None;
        self.access_token = None;
        self.selected_organization_id = None;
        self.navigation_mode = NavigationMode::Scroll;
        self.navigation_settings_status.clear();
        self.login_password.clear();
        self.login_status = "Abgemeldet.".to_string();
        self.view = AppView::Login;
    }

    fn load_user_navigation_settings(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let result = get_user_navigation_settings(&base, &token).await;
                let _ = sender.send(AdminEvent::NavigationSettingsLoaded(result));
            });
        });
    }

    fn save_navigation_mode(&mut self, mode: NavigationMode) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        self.navigation_mode = mode;
        self.navigation_settings_status = "Speichere Menü...".to_string();
        let base = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let result =
                    put_user_navigation_settings(&base, &token, UserNavigationSettings { mode })
                        .await;
                let _ = sender.send(AdminEvent::NavigationSettingsSaved(result));
            });
        });
    }

    fn submit_dev_bootstrap(&mut self) {
        let organization_name = self.dev_organization_name.trim().to_string();
        let email = self.dev_email.trim().to_string();

        if organization_name.len() < 2 {
            self.dev_status = "Firmenname ist zu kurz.".to_string();
            return;
        }
        if !email.contains('@') {
            self.dev_status = "E-Mail-Adresse ist ungültig.".to_string();
            return;
        }

        self.dev_pending = true;
        self.dev_status = "Lege lokale Testfirma an...".to_string();
        self.dev_credentials = None;

        let api_base_url = self.api_base_url.clone();
        let sender = self.dev_bootstrap_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = dev_bootstrap_local(&api_base_url, organization_name, email).await;
                let _ = sender.send(result);
            });
        });
    }

    fn submit_registration(&mut self) {
        let organization_name = self.organization_name.trim().to_string();
        let email = self.registration_email.trim().to_string();

        if organization_name.len() < 2 {
            self.registration_status = "Firmenname ist zu kurz.".to_string();
            return;
        }

        if !email.contains('@') {
            self.registration_status = "E-Mail-Adresse ist ungültig.".to_string();
            return;
        }

        if !self.accept_terms {
            self.registration_status = "Nutzungsbedingungen müssen akzeptiert werden.".to_string();
            return;
        }

        self.registration_pending = true;
        self.registration_status = "Sende Registrierung...".to_string();

        let api_base_url = self.api_base_url.clone();
        let sender = self.registration_sender.clone();
        let accept_terms = self.accept_terms;

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result =
                    register_organization(&api_base_url, organization_name, email, accept_terms)
                        .await;
                let _ = sender.send(result);
            });
        });
    }

    fn load_users(&mut self) {
        self.users_pending = true;
        self.users_status = "Lade Benutzer...".to_string();
        let Some(access_token) = self.access_token.clone() else {
            self.users_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.user_admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = list_users(&api_base_url, &access_token).await;
                let _ = sender.send(UserAdminEvent::UsersLoaded(result));
            });
        });
    }

    fn invite_user(&mut self) {
        let email = self.invite_email.trim().to_string();
        if !email.contains('@') {
            self.users_status = "E-Mail-Adresse ist ungültig.".to_string();
            return;
        }

        self.users_pending = true;
        self.users_status = "Sende Einladung...".to_string();
        let Some(access_token) = self.access_token.clone() else {
            self.users_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let roles = normalized_roles(&self.invite_roles);
        let sender = self.user_admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = invite_organization_user(&api_base_url, &access_token, email, roles)
                    .await
                    .map(|_| "Einladung angelegt.".to_string());
                let _ = sender.send(UserAdminEvent::InvitationSent(result));
            });
        });
    }

    fn save_user_roles(&mut self, user_id: String, roles: Vec<String>) {
        self.saving_user_id = Some(user_id.clone());
        self.users_status = "Speichere Benutzerrechte...".to_string();
        let Some(access_token) = self.access_token.clone() else {
            self.users_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.user_admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = update_user_roles(
                    &api_base_url,
                    &access_token,
                    &user_id,
                    normalized_roles(&roles),
                )
                .await;
                let _ = sender.send(UserAdminEvent::RolesSaved { user_id, result });
            });
        });
    }

    fn save_organization_setup(&mut self) {
        self.organization_setup_pending = true;
        self.organization_setup_status = "Speichere Firmendaten...".to_string();
        let Some(access_token) = self.access_token.clone() else {
            self.organization_setup_pending = false;
            self.organization_setup_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let payload = self.organization_setup.clone();
        let sender = self.admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = save_organization_setup(&api_base_url, &access_token, payload).await;
                let _ = sender.send(AdminEvent::OrganizationSetupSaved(result));
            });
        });
    }

    fn load_organization_setup(&mut self) {
        self.organization_setup_pending = true;
        self.organization_setup_status = "Lade Firmendaten...".to_string();
        let Some(access_token) = self.access_token.clone() else {
            self.organization_setup_pending = false;
            self.organization_setup_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = load_organization_setup(&api_base_url, &access_token).await;
                let _ = sender.send(AdminEvent::OrganizationSetupLoaded(result));
            });
        });
    }

    fn load_registrations(&mut self) {
        self.registrations_pending = true;
        self.registrations_status = "Lade Registrierungen...".to_string();
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = list_registrations(&api_base_url).await;
                let _ = sender.send(AdminEvent::RegistrationsLoaded(result));
            });
        });
    }

    fn approve_registration(&mut self, registration_id: String) {
        self.registrations_pending = true;
        self.registrations_status = "Schalte Registrierung frei...".to_string();
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();

        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = approve_registration(&api_base_url, &registration_id)
                    .await
                    .map(|response| {
                        format!(
                            "Freigeschaltet. Initialpasswort: {}",
                            response.dev_initial_password
                        )
                    });
                let _ = sender.send(AdminEvent::RegistrationActionDone(result));
            });
        });
    }

    fn load_customers(&mut self) {
        self.customers_pending = true;
        let Some(access_token) = self.access_token.clone() else {
            self.customers_status = "Anmeldung erforderlich.".to_string();
            self.customers_pending = false;
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = list_customers(&api_base_url, &access_token).await;
                let _ = sender.send(AdminEvent::CustomersLoaded(result));
            });
        });
    }

    fn new_customer(&mut self) {
        self.selected_customer_id = None;
        self.customer_form = CustomerForm::default();
        self.customers_status = "Kundennummer wird reserviert.".to_string();
        self.reserve_next_number("customers");
    }

    fn save_customer(&mut self) {
        self.customers_pending = true;
        let Some(access_token) = self.access_token.clone() else {
            self.customers_status = "Anmeldung erforderlich.".to_string();
            self.customers_pending = false;
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let customer_id = self.selected_customer_id.clone();
        let form = self.customer_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = save_customer(&api_base_url, &access_token, customer_id, form).await;
                let _ = sender.send(AdminEvent::CustomerSaved(result));
            });
        });
    }

    fn deactivate_customer(&mut self) {
        let Some(customer_id) = self.selected_customer_id.clone() else {
            return;
        };
        let Some(access_token) = self.access_token.clone() else {
            self.customers_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        self.customers_pending = true;
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = delete_customer(&api_base_url, &access_token, &customer_id).await;
                let _ = sender.send(AdminEvent::CustomerDeleted(result));
            });
        });
    }

    fn load_suppliers(&mut self) {
        self.spawn_master_load("/api/v1/suppliers", |result| {
            AdminEvent::SuppliersLoaded(result)
        });
    }

    fn load_number_ranges(&mut self) {
        self.spawn_master_load("/api/v1/number-ranges", |result| {
            AdminEvent::NumberRangesLoaded(result)
        });
    }

    fn load_cash_discount_terms(&mut self) {
        self.spawn_master_load("/api/v1/cash-discount-terms", |result| {
            AdminEvent::CashDiscountTermsLoaded(result)
        });
    }

    fn load_items(&mut self) {
        self.spawn_master_load("/api/v1/items", |result| AdminEvent::ItemsLoaded(result));
    }

    fn load_item_price_history(&mut self, item_id: String) {
        let path = format!("/api/v1/items/{item_id}/prices");
        self.spawn_owned_master_load(path, |result| AdminEvent::ItemPriceHistoryLoaded(result));
    }

    fn load_activities(&mut self) {
        self.spawn_master_load("/api/v1/activities", |result| {
            AdminEvent::ActivitiesLoaded(result)
        });
    }

    fn load_quotes(&mut self) {
        self.spawn_master_load("/api/v1/quotes", |result| AdminEvent::QuotesLoaded(result));
    }

    fn load_outgoing_invoices(&mut self) {
        self.spawn_master_load("/api/v1/outgoing-invoices", |result| {
            AdminEvent::OutgoingInvoicesLoaded(result)
        });
    }

    fn load_incoming_invoices(&mut self) {
        self.spawn_master_load("/api/v1/incoming-invoices", |result| {
            AdminEvent::IncomingInvoicesLoaded(result)
        });
    }

    fn reserve_next_number(&mut self, code: &'static str) {
        let Some(access_token) = self.access_token.clone() else {
            self.set_number_status(code, "Anmeldung erforderlich.".to_string());
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = reserve_next_number(&api_base_url, &access_token, code).await;
                let _ = sender.send(AdminEvent::NextNumberReserved {
                    code: code.to_string(),
                    result,
                });
            });
        });
    }

    fn apply_reserved_number(&mut self, code: &str, number: String) {
        match code {
            "customers" => {
                if self.selected_customer_id.is_none() {
                    self.customer_form.customer_number = number;
                    self.customers_status = "Neue Kundennummer reserviert.".to_string();
                }
            }
            "suppliers" => {
                if self.selected_supplier_id.is_none() {
                    self.supplier_form.supplier_number = number;
                    self.suppliers_status = "Neue Lieferantennummer reserviert.".to_string();
                }
            }
            "items" => {
                if self.selected_item_id.is_none() {
                    self.item_form.item_number = number;
                    self.items_status = "Neue Artikelnummer reserviert.".to_string();
                }
            }
            "activities" => {
                if self.selected_activity_id.is_none() {
                    self.activity_form.activity_number = Some(number);
                    self.activities_status = "Neue Aktivitätsnummer reserviert.".to_string();
                }
            }
            "quotes" => {
                if self.selected_quote_id.is_none() {
                    self.quote_form.quote_number = number;
                    self.quotes_status = "Neue Angebotsnummer reserviert.".to_string();
                }
            }
            "outgoing_invoices" => {
                if self.selected_outgoing_invoice_id.is_none() {
                    self.outgoing_invoice_form.invoice_number = number;
                    self.outgoing_invoices_status = "Neue Rechnungsnummer reserviert.".to_string();
                }
            }
            "incoming_invoices" => {
                if self.selected_incoming_invoice_id.is_none() {
                    self.incoming_invoice_form.invoice_number = number;
                    self.incoming_invoices_status =
                        "Neue Eingangsrechnungsnummer reserviert.".to_string();
                }
            }
            _ => {}
        }
    }

    fn set_number_status(&mut self, code: &str, message: String) {
        match code {
            "customers" => self.customers_status = message,
            "suppliers" => self.suppliers_status = message,
            "items" => self.items_status = message,
            "activities" => self.activities_status = message,
            "quotes" => self.quotes_status = message,
            "outgoing_invoices" => self.outgoing_invoices_status = message,
            "incoming_invoices" => self.incoming_invoices_status = message,
            _ => self.number_ranges_status = message,
        }
    }

    fn load_api_connectors(&mut self) {
        self.spawn_master_load("/api/v1/api-connectors", |result| {
            AdminEvent::ApiConnectorsLoaded(result)
        });
    }

    fn load_price_rules(&mut self) {
        self.spawn_master_load("/api/v1/price-rules", |result| {
            AdminEvent::PriceRulesLoaded(result)
        });
    }

    fn spawn_master_load<T, F>(&mut self, path: &'static str, event: F)
    where
        T: for<'de> Deserialize<'de> + Send + 'static,
        F: FnOnce(Result<Vec<T>, String>) -> AdminEvent + Send + 'static,
    {
        let Some(access_token) = self.access_token.clone() else {
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = list_master_records(&api_base_url, &access_token, path).await;
                let _ = sender.send(event(result));
            });
        });
    }

    fn spawn_owned_master_load<T, F>(&mut self, path: String, event: F)
    where
        T: for<'de> Deserialize<'de> + Send + 'static,
        F: FnOnce(Result<Vec<T>, String>) -> AdminEvent + Send + 'static,
    {
        let Some(access_token) = self.access_token.clone() else {
            return;
        };
        let api_base_url = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().expect("tokio runtime");
            runtime.block_on(async move {
                let result = list_master_records(&api_base_url, &access_token, &path).await;
                let _ = sender.send(event(result));
            });
        });
    }

    fn save_supplier(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_supplier_id.clone();
        let form = self.supplier_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::SupplierSaved(
                    save_master_record(&base, &token, "/api/v1/suppliers", id, form).await,
                ));
            });
        });
    }

    fn save_cash_discount_term(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_cash_discount_term_id.clone();
        let form = self.cash_discount_term_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::CashDiscountTermSaved(
                    save_master_record(&base, &token, "/api/v1/cash-discount-terms", id, form)
                        .await,
                ));
            });
        });
    }

    fn save_number_range(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let Some(code) = self.selected_number_range_code.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let form = self.number_range_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let path = format!("/api/v1/number-ranges/{code}");
                let _ = sender.send(AdminEvent::NumberRangeSaved(
                    save_master_record_without_id(&base, &token, &path, form).await,
                ));
            });
        });
    }

    fn save_item(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_item_id.clone();
        let form = self.item_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::ItemSaved(
                    save_master_record(&base, &token, "/api/v1/items", id, form).await,
                ));
            });
        });
    }

    fn save_activity(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_activity_id.clone();
        let form = self.activity_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::ActivitySaved(
                    save_master_record(&base, &token, "/api/v1/activities", id, form).await,
                ));
            });
        });
    }

    fn preview_price_import(&mut self) {
        let Some(token) = self.access_token.clone() else {
            self.price_import_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let base = self.api_base_url.clone();
        let form = self.price_import_form.clone();
        let sender = self.admin_sender.clone();
        self.price_import_status = "Prüfe Preisliste...".to_string();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::PriceImportPreviewed(
                    post_master_record(&base, &token, "/api/v1/imports/price-list/preview", form)
                        .await,
                ));
            });
        });
    }

    fn apply_price_import(&mut self) {
        let Some(token) = self.access_token.clone() else {
            self.price_import_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let base = self.api_base_url.clone();
        let form = self.price_import_form.clone();
        let sender = self.admin_sender.clone();
        self.price_import_status = "Importiere Preisliste...".to_string();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::PriceImportApplied(
                    post_master_record(&base, &token, "/api/v1/imports/price-list/apply", form)
                        .await,
                ));
            });
        });
    }

    fn save_api_connector(&mut self) {
        let Some(token) = self.access_token.clone() else {
            self.api_connectors_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let Ok(payload) = self.api_connector_form.payload() else {
            self.api_connectors_status = "Konfiguration ist kein gültiges JSON.".to_string();
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_api_connector_id.clone();
        let sender = self.admin_sender.clone();
        self.api_connectors_status = "Speichere Preis-API...".to_string();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::ApiConnectorSaved(
                    save_master_record(&base, &token, "/api/v1/api-connectors", id, payload).await,
                ));
            });
        });
    }

    fn sync_api_connector(&mut self) {
        let Some(token) = self.access_token.clone() else {
            self.api_connectors_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let Some(id) = self.selected_api_connector_id.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        self.api_connectors_status = "Führe Preisabgleich aus...".to_string();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let path = format!("/api/v1/api-connectors/{id}/sync");
                let _ = sender.send(AdminEvent::ApiConnectorSynced(
                    post_master_record(&base, &token, &path, serde_json::json!({})).await,
                ));
            });
        });
    }

    fn save_price_rule(&mut self) {
        let Some(token) = self.access_token.clone() else {
            self.price_rules_status = "Anmeldung erforderlich.".to_string();
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_price_rule_id.clone();
        let mut form = self.price_rule_form.clone();
        if form
            .source_id
            .as_deref()
            .is_some_and(|value| value.trim().is_empty())
        {
            form.source_id = None;
        }
        let sender = self.admin_sender.clone();
        self.price_rules_status = "Speichere Preisregel...".to_string();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::PriceRuleSaved(
                    save_master_record(&base, &token, "/api/v1/price-rules", id, form).await,
                ));
            });
        });
    }

    fn save_quote(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_quote_id.clone();
        let form = self.quote_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::QuoteSaved(
                    save_master_record(&base, &token, "/api/v1/quotes", id, form).await,
                ));
            });
        });
    }

    fn save_outgoing_invoice(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_outgoing_invoice_id.clone();
        let form = self.outgoing_invoice_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::OutgoingInvoiceSaved(
                    save_master_record(&base, &token, "/api/v1/outgoing-invoices", id, form).await,
                ));
            });
        });
    }

    fn save_incoming_invoice(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let id = self.selected_incoming_invoice_id.clone();
        let form = self.incoming_invoice_form.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(AdminEvent::IncomingInvoiceSaved(
                    save_master_record(&base, &token, "/api/v1/incoming-invoices", id, form).await,
                ));
            });
        });
    }

    fn delete_supplier(&mut self) {
        if let Some(id) = self.selected_supplier_id.clone() {
            self.spawn_master_delete("/api/v1/suppliers", id, AdminEvent::SupplierDeleted);
        }
    }
    fn delete_cash_discount_term(&mut self) {
        if let Some(id) = self.selected_cash_discount_term_id.clone() {
            self.spawn_master_delete(
                "/api/v1/cash-discount-terms",
                id,
                AdminEvent::CashDiscountTermDeleted,
            );
        }
    }
    fn delete_item(&mut self) {
        if let Some(id) = self.selected_item_id.clone() {
            self.spawn_master_delete("/api/v1/items", id, AdminEvent::ItemDeleted);
        }
    }
    fn delete_quote(&mut self) {
        if let Some(id) = self.selected_quote_id.clone() {
            self.spawn_master_delete("/api/v1/quotes", id, AdminEvent::QuoteDeleted);
        }
    }
    fn delete_outgoing_invoice(&mut self) {
        if let Some(id) = self.selected_outgoing_invoice_id.clone() {
            self.spawn_master_delete(
                "/api/v1/outgoing-invoices",
                id,
                AdminEvent::OutgoingInvoiceDeleted,
            );
        }
    }
    fn finalize_outgoing_invoice(&mut self) {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let Some(id) = self.selected_outgoing_invoice_id.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let path = format!("/api/v1/outgoing-invoices/{id}/finalize");
                let _ = sender.send(AdminEvent::OutgoingInvoiceFinalized(
                    post_empty_action(&base, &token, &path).await,
                ));
            });
        });
    }
    fn delete_incoming_invoice(&mut self) {
        if let Some(id) = self.selected_incoming_invoice_id.clone() {
            self.spawn_master_delete(
                "/api/v1/incoming-invoices",
                id,
                AdminEvent::IncomingInvoiceDeleted,
            );
        }
    }
    fn delete_activity(&mut self) {
        if let Some(id) = self.selected_activity_id.clone() {
            self.spawn_master_delete("/api/v1/activities", id, AdminEvent::ActivityDeleted);
        }
    }
    fn delete_api_connector(&mut self) {
        if let Some(id) = self.selected_api_connector_id.clone() {
            self.spawn_master_delete(
                "/api/v1/api-connectors",
                id,
                AdminEvent::ApiConnectorDeleted,
            );
        }
    }
    fn delete_price_rule(&mut self) {
        if let Some(id) = self.selected_price_rule_id.clone() {
            self.spawn_master_delete("/api/v1/price-rules", id, AdminEvent::PriceRuleDeleted);
        }
    }

    fn open_dashboard(&mut self) {
        self.view = AppView::Status;
    }

    fn open_outgoing_invoices_window(&mut self) {
        self.restore_window("outgoing_invoices");
        self.outgoing_invoices_window_open = true;
        self.load_outgoing_invoices();
        self.load_customers();
        self.load_items();
    }

    fn open_quotes_window(&mut self) {
        self.restore_window("quotes");
        self.quotes_window_open = true;
        self.load_quotes();
        self.load_customers();
        self.load_items();
    }

    fn open_incoming_invoices_window(&mut self) {
        self.restore_window("incoming_invoices");
        self.incoming_invoices_window_open = true;
        self.load_incoming_invoices();
        self.load_suppliers();
        self.load_items();
    }

    fn open_customers_window(&mut self) {
        self.restore_window("customers");
        self.customers_window_open = true;
        self.load_customers();
        self.load_cash_discount_terms();
    }

    fn open_suppliers_window(&mut self) {
        self.restore_window("suppliers");
        self.suppliers_window_open = true;
        self.load_suppliers();
        self.load_cash_discount_terms();
    }

    fn open_items_window(&mut self) {
        self.restore_window("items");
        self.items_window_open = true;
        self.load_items();
        self.load_suppliers();
    }

    fn open_activities_window(&mut self) {
        self.restore_window("activities");
        self.activities_window_open = true;
        self.load_activities();
    }

    fn open_price_import_window(&mut self) {
        self.restore_window("price_imports");
        self.price_import_window_open = true;
    }

    fn open_price_rules_window(&mut self) {
        self.restore_window("price_rules");
        self.price_rules_window_open = true;
        self.load_price_rules();
    }

    fn open_api_connectors_window(&mut self) {
        self.restore_window("api_connectors");
        self.api_connectors_window_open = true;
        self.load_api_connectors();
    }

    fn open_organization_setup_window(&mut self) {
        self.restore_window("organization_setup");
        self.organization_setup_window_open = true;
        self.load_organization_setup();
    }

    fn open_users_window(&mut self) {
        self.restore_window("users");
        self.users_window_open = true;
        if self.users.is_empty() {
            self.load_users();
        }
    }

    fn open_number_ranges_window(&mut self) {
        self.restore_window("number_ranges");
        self.number_ranges_window_open = true;
        self.load_number_ranges();
    }

    fn open_cash_discount_terms_window(&mut self) {
        self.restore_window("cash_discount_terms");
        self.cash_discount_terms_window_open = true;
        self.load_cash_discount_terms();
    }

    fn open_admin_registrations_window(&mut self) {
        self.restore_window("admin_registrations");
        self.admin_registrations_window_open = true;
        if self.registrations.is_empty() {
            self.load_registrations();
        }
    }

    fn minimize_window(&mut self, key: &'static str) {
        self.minimized_windows.insert(key);
    }

    fn restore_window(&mut self, key: &'static str) {
        self.minimized_windows.remove(key);
    }

    fn window_is_minimized(&self, key: &'static str) -> bool {
        self.minimized_windows.contains(key)
    }

    fn clear_minimized_if_closed(&mut self, key: &'static str, open: bool) {
        if !open {
            self.minimized_windows.remove(key);
        }
    }

    fn nav_dropdown(
        &mut self,
        ui: &mut egui::Ui,
        label: &str,
        items: &[(&str, fn(&mut CompanyToolApp))],
        add_top_space: bool,
    ) {
        if add_top_space {
            ui.add_space(12.0);
        }
        ui.scope(|ui| {
            let visuals = ui.visuals_mut();
            visuals.widgets.inactive.bg_fill = accent_soft_color();
            visuals.widgets.inactive.fg_stroke = egui::Stroke::new(1.0, accent_dark_color());
            visuals.widgets.hovered.bg_fill = egui::Color32::from_rgb(199, 235, 230);
            visuals.widgets.hovered.fg_stroke = egui::Stroke::new(1.0, accent_dark_color());
            visuals.widgets.active.bg_fill = accent_soft_color();
            visuals.widgets.active.fg_stroke = egui::Stroke::new(1.0, accent_dark_color());
            visuals.widgets.open.bg_fill = accent_soft_color();
            visuals.widgets.open.fg_stroke = egui::Stroke::new(1.0, accent_dark_color());
            ui.set_min_width(200.0);
            ui.menu_button(
                egui::RichText::new(label)
                    .color(accent_dark_color())
                    .strong(),
                |ui| {
                    let visuals = ui.visuals_mut();
                    visuals.widgets.inactive.bg_fill = surface_color();
                    visuals.widgets.inactive.fg_stroke =
                        egui::Stroke::new(1.0, accent_dark_color());
                    visuals.widgets.hovered.bg_fill = accent_soft_color();
                    visuals.widgets.hovered.fg_stroke = egui::Stroke::new(1.0, accent_dark_color());
                    visuals.widgets.active.bg_fill = accent_color();
                    visuals.widgets.active.fg_stroke = egui::Stroke::new(1.0, egui::Color32::WHITE);
                    ui.set_min_width(220.0);
                    for (item_label, action) in items {
                        let (rect, response) = ui.allocate_exact_size(
                            egui::vec2(ui.available_width(), 24.0),
                            egui::Sense::click(),
                        );
                        let active = response.hovered() || response.has_focus();
                        let bg = if active {
                            accent_dark_color()
                        } else {
                            surface_color()
                        };
                        let fg = if active {
                            egui::Color32::WHITE
                        } else {
                            accent_dark_color()
                        };
                        ui.painter().rect_filled(rect, 4.0, bg);
                        ui.painter().text(
                            rect.left_center() + egui::vec2(8.0, 0.0),
                            egui::Align2::LEFT_CENTER,
                            *item_label,
                            egui::FontId::proportional(13.0),
                            fg,
                        );
                        if response.clicked() {
                            action(self);
                            ui.close_menu();
                        }
                    }
                },
            );
        });
    }

    fn render_taskbar(&mut self, ui: &mut egui::Ui) {
        ui.horizontal_wrapped(|ui| {
            self.taskbar_button(ui, "users", self.users_window_open, "Benutzerrechte");
            self.taskbar_button(
                ui,
                "organization_setup",
                self.organization_setup_window_open,
                "Firmendaten",
            );
            self.taskbar_button(
                ui,
                "number_ranges",
                self.number_ranges_window_open,
                "Nummernkreise",
            );
            self.taskbar_button(
                ui,
                "admin_registrations",
                self.admin_registrations_window_open,
                "Freischaltung",
            );
            self.taskbar_button(ui, "customers", self.customers_window_open, "Kunden");
            self.taskbar_button(ui, "suppliers", self.suppliers_window_open, "Lieferanten");
            self.taskbar_button(ui, "items", self.items_window_open, "Artikel");
            self.taskbar_button(
                ui,
                "price_imports",
                self.price_import_window_open,
                "Preislisten",
            );
            self.taskbar_button(
                ui,
                "api_connectors",
                self.api_connectors_window_open,
                "Preis-APIs",
            );
            self.taskbar_button(
                ui,
                "price_rules",
                self.price_rules_window_open,
                "Preisregeln",
            );
            self.taskbar_button(
                ui,
                "cash_discount_terms",
                self.cash_discount_terms_window_open,
                "Skonto",
            );
            self.taskbar_button(ui, "quotes", self.quotes_window_open, "Angebote");
            self.taskbar_button(
                ui,
                "outgoing_invoices",
                self.outgoing_invoices_window_open,
                "Ausgangsrechnungen",
            );
            self.taskbar_button(
                ui,
                "incoming_invoices",
                self.incoming_invoices_window_open,
                "Eingangsrechnungen",
            );
            self.taskbar_button(ui, "activities", self.activities_window_open, "Aktivitäten");
        });
    }

    fn taskbar_button(&mut self, ui: &mut egui::Ui, key: &'static str, open: bool, label: &str) {
        if !open {
            return;
        }
        let minimized = self.window_is_minimized(key);
        let button = egui::Button::new(
            egui::RichText::new(label)
                .color(if minimized {
                    accent_dark_color()
                } else {
                    egui::Color32::WHITE
                })
                .size(12.0)
                .strong(),
        )
        .fill(if minimized {
            accent_soft_color()
        } else {
            accent_dark_color()
        })
        .rounding(6.0);
        if ui.add_sized([170.0, 30.0], button).clicked() {
            self.restore_window(key);
        }
    }

    fn spawn_master_delete<F>(&mut self, path: &'static str, id: String, event: F)
    where
        F: FnOnce(EmptyResult) -> AdminEvent + Send + 'static,
    {
        let Some(token) = self.access_token.clone() else {
            return;
        };
        let base = self.api_base_url.clone();
        let sender = self.admin_sender.clone();
        std::thread::spawn(move || {
            let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
            rt.block_on(async move {
                let _ = sender.send(event(delete_master_record(&base, &token, path, &id).await));
            });
        });
    }
}

impl eframe::App for CompanyToolApp {
    fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
        apply_app_style(ctx);
        self.drain_messages();
        ctx.request_repaint_after(std::time::Duration::from_millis(250));

        egui::SidePanel::left("navigation")
            .exact_width(244.0)
            .frame(egui::Frame::side_top_panel(&ctx.style()).fill(surface_color()))
            .show(ctx, |ui| {
                ui.add_space(10.0);
                brand(ui, self.logo_texture.as_ref());
                ui.add_space(20.0);

                let nav_height = if self.authenticated_email.is_some() {
                    (ui.available_height() - 170.0).max(180.0)
                } else {
                    ui.available_height()
                };
                let nav_button_height = self.navigation_mode.button_height();

                egui::ScrollArea::vertical()
                    .id_salt("navigation_menu")
                    .max_height(nav_height)
                    .auto_shrink([false, false])
                    .show(ui, |ui| {
                        if self.authenticated_email.is_some() {
                            if self.navigation_mode == NavigationMode::Groups {
                                self.nav_dropdown(
                                    ui,
                                    "Vorgänge",
                                    &[
                                        ("Dashboard", CompanyToolApp::open_dashboard),
                                        (
                                            "Ausgangsrechnungen",
                                            CompanyToolApp::open_outgoing_invoices_window,
                                        ),
                                        ("Angebote", CompanyToolApp::open_quotes_window),
                                        (
                                            "Eingangsrechnungen",
                                            CompanyToolApp::open_incoming_invoices_window,
                                        ),
                                    ],
                                    false,
                                );
                                self.nav_dropdown(
                                    ui,
                                    "Stammdaten",
                                    &[
                                        ("Kunden", CompanyToolApp::open_customers_window),
                                        ("Lieferanten", CompanyToolApp::open_suppliers_window),
                                        ("Artikel", CompanyToolApp::open_items_window),
                                        ("Aktivitäten", CompanyToolApp::open_activities_window),
                                    ],
                                    true,
                                );
                                self.nav_dropdown(
                                    ui,
                                    "Arbeitsdaten",
                                    &[("Preislisten", CompanyToolApp::open_price_import_window)],
                                    true,
                                );
                                self.nav_dropdown(
                                    ui,
                                    "Einstellungen",
                                    &[
                                        ("Preisregeln", CompanyToolApp::open_price_rules_window),
                                        ("Preis-APIs", CompanyToolApp::open_api_connectors_window),
                                        (
                                            "Firmendaten",
                                            CompanyToolApp::open_organization_setup_window,
                                        ),
                                        ("Benutzerrechte", CompanyToolApp::open_users_window),
                                        (
                                            "Nummernkreise",
                                            CompanyToolApp::open_number_ranges_window,
                                        ),
                                        ("Skonto", CompanyToolApp::open_cash_discount_terms_window),
                                        (
                                            "Freischaltung",
                                            CompanyToolApp::open_admin_registrations_window,
                                        ),
                                    ],
                                    true,
                                );
                            } else {
                                nav_group_label(ui, "Vorgänge");
                                self.nav_button(ui, AppView::Status, "Dashboard");
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Ausgangsrechnungen"),
                                    )
                                    .clicked()
                                {
                                    self.open_outgoing_invoices_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Angebote"),
                                    )
                                    .clicked()
                                {
                                    self.open_quotes_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Eingangsrechnungen"),
                                    )
                                    .clicked()
                                {
                                    self.open_incoming_invoices_window();
                                }

                                nav_group_label(ui, "Stammdaten");
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Kunden"),
                                    )
                                    .clicked()
                                {
                                    self.open_customers_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Lieferanten"),
                                    )
                                    .clicked()
                                {
                                    self.open_suppliers_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Artikel"),
                                    )
                                    .clicked()
                                {
                                    self.open_items_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Aktivitäten"),
                                    )
                                    .clicked()
                                {
                                    self.open_activities_window();
                                }

                                nav_group_label(ui, "Arbeitsdaten");
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Preislisten"),
                                    )
                                    .clicked()
                                {
                                    self.open_price_import_window();
                                }

                                nav_group_label(ui, "Einstellungen");
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Preisregeln"),
                                    )
                                    .clicked()
                                {
                                    self.open_price_rules_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Preis-APIs"),
                                    )
                                    .clicked()
                                {
                                    self.open_api_connectors_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Firmendaten"),
                                    )
                                    .clicked()
                                {
                                    self.open_organization_setup_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Benutzerrechte"),
                                    )
                                    .clicked()
                                {
                                    self.open_users_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Nummernkreise"),
                                    )
                                    .clicked()
                                {
                                    self.open_number_ranges_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Skonto"),
                                    )
                                    .clicked()
                                {
                                    self.open_cash_discount_terms_window();
                                }
                                if ui
                                    .add_sized(
                                        [200.0, nav_button_height],
                                        sidebar_action_button("Freischaltung"),
                                    )
                                    .clicked()
                                {
                                    self.open_admin_registrations_window();
                                }
                            }
                        } else {
                            self.nav_button(ui, AppView::Login, "Login");
                            self.nav_button(ui, AppView::Register, "Registrierung");
                        }
                    });

                if let Some(email) = self.authenticated_email.clone() {
                    ui.add_space(22.0);
                    ui.separator();
                    ui.add_space(12.0);
                    let mut selected_mode = self.navigation_mode;
                    ui.scope(|ui| {
                        let visuals = ui.visuals_mut();
                        visuals.widgets.inactive.bg_fill = accent_soft_color();
                        visuals.widgets.inactive.fg_stroke =
                            egui::Stroke::new(1.0, accent_dark_color());
                        visuals.widgets.hovered.bg_fill = egui::Color32::from_rgb(199, 235, 230);
                        visuals.widgets.hovered.fg_stroke =
                            egui::Stroke::new(1.0, accent_dark_color());
                        visuals.widgets.active.bg_fill = accent_soft_color();
                        visuals.widgets.active.fg_stroke =
                            egui::Stroke::new(1.0, accent_dark_color());
                        visuals.widgets.open.bg_fill = accent_soft_color();
                        visuals.widgets.open.fg_stroke =
                            egui::Stroke::new(1.0, accent_dark_color());
                        egui::ComboBox::from_id_salt("navigation_mode")
                            .selected_text(
                                egui::RichText::new(selected_mode.label())
                                    .color(accent_dark_color())
                                    .strong(),
                            )
                            .show_ui(ui, |ui| {
                                let visuals = ui.visuals_mut();
                                visuals.widgets.inactive.bg_fill = surface_color();
                                visuals.widgets.inactive.fg_stroke =
                                    egui::Stroke::new(1.0, accent_dark_color());
                                visuals.widgets.hovered.bg_fill = accent_soft_color();
                                visuals.widgets.hovered.fg_stroke =
                                    egui::Stroke::new(1.0, accent_dark_color());
                                visuals.widgets.active.bg_fill = accent_color();
                                visuals.widgets.active.fg_stroke =
                                    egui::Stroke::new(1.0, egui::Color32::WHITE);
                                ui.selectable_value(
                                    &mut selected_mode,
                                    NavigationMode::Scroll,
                                    egui::RichText::new(NavigationMode::Scroll.label())
                                        .color(accent_dark_color())
                                        .strong(),
                                );
                                ui.selectable_value(
                                    &mut selected_mode,
                                    NavigationMode::Groups,
                                    egui::RichText::new(NavigationMode::Groups.label())
                                        .color(accent_dark_color())
                                        .strong(),
                                );
                            });
                    });
                    if selected_mode != self.navigation_mode {
                        self.save_navigation_mode(selected_mode);
                    }
                    if !self.navigation_settings_status.is_empty() {
                        ui.label(
                            egui::RichText::new(&self.navigation_settings_status)
                                .color(muted_text_color())
                                .size(12.0),
                        );
                    }
                    ui.add_space(8.0);
                    ui.label(
                        egui::RichText::new(email)
                            .color(muted_text_color())
                            .size(13.0)
                            .strong(),
                    );
                    ui.add_space(8.0);
                    if ui
                        .add_sized([180.0, 34.0], sidebar_action_button("Abmelden"))
                        .clicked()
                    {
                        self.logout();
                    }
                }
            });

        egui::TopBottomPanel::bottom("window_taskbar")
            .exact_height(46.0)
            .frame(egui::Frame::side_top_panel(&ctx.style()).fill(surface_color()))
            .show(ctx, |ui| {
                ui.add_space(6.0);
                self.render_taskbar(ui);
            });

        egui::CentralPanel::default()
            .frame(egui::Frame::central_panel(&ctx.style()).fill(background_color()))
            .show(ctx, |ui| {
                ui.add_space(12.0);
                egui::ScrollArea::vertical().show(ui, |ui| match self.view {
                    AppView::Login => self.login_view(ui),
                    AppView::Status => self.status_view(ui),
                    AppView::Register => self.registration_view(ui),
                });
            });

        macro_rules! managed_window {
            ($key:literal, $field:ident, $title:literal, $body:expr $(, $method:ident($($arg:expr),*))* $(,)?) => {
                if self.$field && !self.window_is_minimized($key) {
                    let mut open = self.$field;
                    let mut minimize = false;
                    let screen = ctx.screen_rect();
                    let window_area = egui::Rect::from_min_max(
                        screen.min,
                        egui::pos2(screen.max.x, screen.max.y - 46.0),
                    );
                    egui::Window::new($title)
                        .open(&mut open)
                        .collapsible(false)
                        .constrain_to(window_area)
                        $(
                            .$method($($arg),*)
                        )*
                        .show(ctx, |ui| {
                            if window_minimize_control(ui) {
                                minimize = true;
                            }
                            egui::ScrollArea::vertical().show(ui, |ui| $body(self, ui));
                        });
                    self.$field = open;
                    self.clear_minimized_if_closed($key, open);
                    if minimize {
                        self.minimize_window($key);
                    }
                }
            };
        }

        managed_window!(
            "users",
            users_window_open,
            "Benutzerrechte",
            |app: &mut CompanyToolApp, ui| app.users_view(ui),
            min_width(760.0),
            default_width(900.0),
            default_height(620.0),
            resizable(true)
        );
        managed_window!(
            "organization_setup",
            organization_setup_window_open,
            "Firmendaten",
            |app: &mut CompanyToolApp, ui| app.organization_setup_view(ui),
            min_width(760.0),
            default_width(900.0),
            default_height(620.0),
            resizable(true)
        );
        managed_window!(
            "number_ranges",
            number_ranges_window_open,
            "Nummernkreise",
            |app: &mut CompanyToolApp, ui| app.number_ranges_view(ui),
            min_width(760.0),
            default_width(900.0),
            default_height(620.0),
            resizable(true)
        );
        managed_window!(
            "admin_registrations",
            admin_registrations_window_open,
            "Freischaltung",
            |app: &mut CompanyToolApp, ui| app.registrations_view(ui),
            min_width(760.0),
            default_width(900.0),
            default_height(620.0),
            resizable(true)
        );
        managed_window!(
            "customers",
            customers_window_open,
            "Kunden",
            |app: &mut CompanyToolApp, ui| app.customers_view(ui),
            min_width(820.0),
            default_width(980.0),
            default_height(700.0),
            resizable(true)
        );
        managed_window!(
            "suppliers",
            suppliers_window_open,
            "Lieferanten",
            |app: &mut CompanyToolApp, ui| app.suppliers_view(ui),
            default_width(980.0)
        );
        managed_window!(
            "items",
            items_window_open,
            "Artikel",
            |app: &mut CompanyToolApp, ui| app.items_view(ui),
            default_width(900.0)
        );
        managed_window!(
            "price_imports",
            price_import_window_open,
            "Preislisten",
            |app: &mut CompanyToolApp, ui| app.price_imports_view(ui),
            default_width(980.0),
            default_height(720.0)
        );
        managed_window!(
            "api_connectors",
            api_connectors_window_open,
            "Preis-APIs",
            |app: &mut CompanyToolApp, ui| app.api_connectors_view(ui),
            default_width(980.0),
            default_height(720.0)
        );
        managed_window!(
            "price_rules",
            price_rules_window_open,
            "Preisregeln",
            |app: &mut CompanyToolApp, ui| app.price_rules_view(ui),
            default_width(940.0),
            default_height(680.0)
        );
        managed_window!(
            "cash_discount_terms",
            cash_discount_terms_window_open,
            "Skonto",
            |app: &mut CompanyToolApp, ui| app.cash_discount_terms_view(ui),
            default_width(900.0)
        );
        managed_window!(
            "quotes",
            quotes_window_open,
            "Angebote",
            |app: &mut CompanyToolApp, ui| app.quotes_view(ui),
            default_width(1080.0),
            default_height(760.0)
        );
        managed_window!(
            "outgoing_invoices",
            outgoing_invoices_window_open,
            "Rechnungen",
            |app: &mut CompanyToolApp, ui| app.outgoing_invoices_view(ui),
            default_width(1080.0),
            default_height(760.0)
        );
        managed_window!(
            "incoming_invoices",
            incoming_invoices_window_open,
            "Eingangsrechnungen",
            |app: &mut CompanyToolApp, ui| app.incoming_invoices_view(ui),
            default_width(1080.0),
            default_height(760.0)
        );
        managed_window!(
            "activities",
            activities_window_open,
            "Aktivitäten",
            |app: &mut CompanyToolApp, ui| app.activities_view(ui),
            default_width(900.0)
        );
    }
}

impl CompanyToolApp {
    fn nav_button(&mut self, ui: &mut egui::Ui, view: AppView, label: &str) {
        let selected = self.view == view;
        let button = egui::Button::new(
            egui::RichText::new(label)
                .color(if selected {
                    accent_dark_color()
                } else {
                    egui::Color32::WHITE
                })
                .strong(),
        )
        .fill(if selected {
            accent_soft_color()
        } else {
            accent_dark_color()
        })
        .stroke(egui::Stroke::NONE)
        .rounding(6.0);

        if ui
            .add_sized([200.0, self.navigation_mode.button_height()], button)
            .clicked()
        {
            self.view = view;
        }
        ui.add_space(4.0);
    }

    fn login_view(&mut self, ui: &mut egui::Ui) {
        page_header(ui, "Login", "Anmeldung mit E-Mail-Adresse und Passwort.");

        panel(ui, |ui| {
            ui.set_max_width(620.0);
            form_row(ui, "E-Mail-Adresse", |ui| {
                ui.text_edit_singleline(&mut self.login_email);
            });
            form_row(ui, "Passwort", |ui| {
                ui.add(egui::TextEdit::singleline(&mut self.login_password).password(true));
            });

            ui.add_space(12.0);
            let submit = ui.add_enabled(!self.login_pending, primary_action_button("Einloggen"));
            if submit.clicked() {
                self.submit_login();
            }

            if !self.login_status.is_empty() {
                ui.add_space(10.0);
                ui.label(egui::RichText::new(&self.login_status).color(muted_text_color()));
            }
        });

        panel(ui, |ui| {
            ui.set_max_width(620.0);
            ui.heading("Dev-Bootstrap");
            ui.label(
                egui::RichText::new(
                    "Lokale Testfirma mit erstem User anlegen, ohne E-Mail-Versand.",
                )
                .color(muted_text_color()),
            );
            ui.add_space(10.0);
            form_row(ui, "Firmenname", |ui| {
                ui.text_edit_singleline(&mut self.dev_organization_name);
            });
            form_row(ui, "E-Mail-Adresse", |ui| {
                ui.text_edit_singleline(&mut self.dev_email);
            });
            let submit = ui.add_enabled(
                !self.dev_pending,
                primary_action_button("Testfirma und User anlegen"),
            );
            if submit.clicked() {
                self.submit_dev_bootstrap();
            }
            if !self.dev_status.is_empty() {
                ui.add_space(10.0);
                ui.label(egui::RichText::new(&self.dev_status).color(muted_text_color()));
            }
            if let Some(credentials) = &self.dev_credentials {
                ui.add_space(10.0);
                ui.monospace(format!("Login: {}", credentials.email));
                ui.monospace(format!("Passwort: {}", credentials.password));
                ui.monospace(format!("User: {}", credentials.user_id));
                ui.monospace(format!("Firma: {}", credentials.organization_id));
                ui.monospace(format!("Schema: {}", credentials.schema_name));
                ui.monospace(format!("Dev-Modus: {}", credentials.dev_mode));
            }
        });
    }

    fn status_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Dashboard",
            "Übersicht über Backend-Verbindung und aktuelle Vorgänge.",
        );

        panel(ui, |ui| {
            ui.horizontal(|ui| {
                ui.vertical(|ui| {
                    ui.heading("Live-Verbindung");
                    ui.label(egui::RichText::new(&self.status).color(muted_text_color()));
                });
                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                    ui.monospace(&self.ws_url);
                });
            });
        });

        panel(ui, |ui| {
            ui.heading("Aktuelle Datensätze");
            ui.add_space(10.0);
            egui::Grid::new("records")
                .striped(true)
                .spacing([28.0, 8.0])
                .show(ui, |ui| {
                    ui.strong("Titel");
                    ui.strong("Aktualisiert");
                    ui.end_row();

                    for record in &self.records {
                        ui.label(&record.title);
                        ui.label(record.updated_at.to_rfc3339());
                        ui.end_row();
                    }
                });
        });
    }

    fn registration_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Firma registrieren",
            "Neue Organization für den SaaS-Betrieb vormerken.",
        );

        panel(ui, |ui| {
            ui.set_max_width(620.0);
            form_row(ui, "Firmenname", |ui| {
                ui.text_edit_singleline(&mut self.organization_name);
            });
            form_row(ui, "E-Mail-Adresse", |ui| {
                ui.text_edit_singleline(&mut self.registration_email);
            });
            ui.checkbox(&mut self.accept_terms, "Nutzungsbedingungen akzeptieren");
            ui.add_space(12.0);

            let submit = ui.add_enabled(
                !self.registration_pending,
                primary_action_button("Registrierung absenden"),
            );
            if submit.clicked() {
                self.submit_registration();
            }

            if !self.registration_status.is_empty() {
                ui.add_space(10.0);
                ui.label(egui::RichText::new(&self.registration_status).color(muted_text_color()));
            }
        });
    }

    fn users_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Benutzerrechte",
            "Benutzer einladen und Rollen verwalten.",
        );

        panel(ui, |ui| {
            ui.set_max_width(720.0);
            form_row(ui, "E-Mail-Adresse", |ui| {
                ui.text_edit_singleline(&mut self.invite_email);
            });
            ui.label(
                egui::RichText::new("Rollen")
                    .strong()
                    .color(muted_text_color()),
            );
            role_checkboxes(ui, &mut self.invite_roles);
            ui.add_space(12.0);
            ui.horizontal(|ui| {
                if ui
                    .add_enabled(
                        !self.users_pending,
                        primary_action_button("Einladung senden"),
                    )
                    .clicked()
                {
                    self.invite_user();
                }
                if ui
                    .add_enabled(!self.users_pending, egui::Button::new("Benutzer laden"))
                    .clicked()
                {
                    self.load_users();
                }
            });
            ui.add_space(10.0);
            ui.label(egui::RichText::new(&self.users_status).color(muted_text_color()));
            ui.label(egui::RichText::new(&self.live_update_status).color(muted_text_color()));
        });

        panel(ui, |ui| {
            ui.heading("Benutzerliste");
            ui.add_space(10.0);
            if self.users.is_empty() {
                ui.label(egui::RichText::new(&self.users_status).color(muted_text_color()));
                return;
            }

            ui.horizontal(|ui| {
                ui.add_sized(
                    [220.0, 22.0],
                    egui::Label::new(egui::RichText::new("E-Mail").strong()),
                );
                ui.add_sized(
                    [80.0, 22.0],
                    egui::Label::new(egui::RichText::new("Status").strong()),
                );
                ui.add_sized(
                    [300.0, 22.0],
                    egui::Label::new(egui::RichText::new("Rollen").strong()),
                );
                ui.add_sized(
                    [100.0, 22.0],
                    egui::Label::new(egui::RichText::new("Aktion").strong()),
                );
            });
            ui.separator();

            for index in 0..self.users.len() {
                ui.horizontal(|ui| {
                    ui.add_sized([220.0, 24.0], egui::Label::new(&self.users[index].email));
                    ui.add_sized([80.0, 24.0], egui::Label::new(&self.users[index].status));
                    ui.vertical(|ui| {
                        ui.set_min_width(300.0);
                        role_checkboxes(ui, &mut self.users[index].roles);
                    });
                    let user_id = self.users[index].user_id.clone();
                    let roles = self.users[index].roles.clone();
                    let saving = self.saving_user_id.as_deref() == Some(user_id.as_str());
                    if ui
                        .add_enabled(!saving, egui::Button::new("Speichern"))
                        .clicked()
                    {
                        self.save_user_roles(user_id, roles);
                    }
                });
                ui.add_space(6.0);
                ui.separator();
                ui.add_space(6.0);
            }
        });
    }

    fn organization_setup_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Firmendaten",
            "Stammdaten der aktiven Organization erfassen.",
        );
        panel(ui, |ui| {
            ui.set_max_width(720.0);
            form_row(ui, "Firmenname", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.display_name);
            });
            form_row(ui, "Rechtsform", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.legal_form);
            });
            form_row(ui, "Straße und Hausnummer", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.street);
            });
            form_row(ui, "PLZ", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.postal_code);
            });
            form_row(ui, "Ort", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.city);
            });
            form_row(ui, "Land", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.country);
            });
            form_row(ui, "USt-IdNr.", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.vat_id);
            });
            form_row(ui, "E-Mail der Firma", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.email);
            });
            form_row(ui, "Telefon", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.phone);
            });
            form_row(ui, "Standard-Steuersatz", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.default_tax_rate);
            });
            form_row(ui, "Standard-Zahlungsziel", |ui| {
                ui.text_edit_singleline(&mut self.organization_setup.default_payment_days);
            });
            if ui
                .add_enabled(
                    !self.organization_setup_pending,
                    primary_action_button("Firmendaten speichern"),
                )
                .clicked()
            {
                self.save_organization_setup();
            }
            if !self.organization_setup_status.is_empty() {
                ui.add_space(10.0);
                ui.label(
                    egui::RichText::new(&self.organization_setup_status).color(muted_text_color()),
                );
            }
            ui.label(egui::RichText::new(&self.live_update_status).color(muted_text_color()));
        });
    }

    fn registrations_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Freischaltungen",
            "Offene und entschiedene Organization-Registrierungen.",
        );
        panel(ui, |ui| {
            if ui
                .add_enabled(
                    !self.registrations_pending,
                    primary_action_button("Aktualisieren"),
                )
                .clicked()
            {
                self.load_registrations();
            }
            ui.add_space(10.0);
            ui.label(egui::RichText::new(&self.registrations_status).color(muted_text_color()));
            ui.label(egui::RichText::new(&self.live_update_status).color(muted_text_color()));
        });

        panel(ui, |ui| {
            ui.heading("Registrierungen");
            ui.add_space(10.0);
            if self.registrations.is_empty() {
                ui.label(egui::RichText::new(&self.registrations_status).color(muted_text_color()));
                return;
            }

            egui::Grid::new("registrations_grid")
                .striped(true)
                .spacing([18.0, 8.0])
                .show(ui, |ui| {
                    ui.strong("Firma");
                    ui.strong("E-Mail");
                    ui.strong("Status");
                    ui.strong("Eingegangen");
                    ui.strong("Aktion");
                    ui.end_row();

                    for registration in self.registrations.clone() {
                        ui.label(registration.organization_name);
                        ui.label(registration.email);
                        ui.label(registration.status.clone());
                        ui.label(registration.requested_at);
                        if ui
                            .add_enabled(
                                !self.registrations_pending
                                    && registration.status == "pending_approval",
                                egui::Button::new("Freischalten"),
                            )
                            .clicked()
                        {
                            self.approve_registration(registration.id);
                        }
                        ui.end_row();
                    }
                });
        });
    }

    fn number_ranges_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Nummernkreise",
            "Individuelle Nummern für Stammdaten, Angebote und Rechnungen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(280.0);
                    for range in self.number_ranges.clone() {
                        if ui
                            .selectable_label(
                                self.selected_number_range_code.as_deref() == Some(&range.code),
                                format!(
                                    "{}  {}  {}",
                                    number_range_label(&range.code),
                                    range.pattern,
                                    &range.id[..range.id.len().min(8)]
                                ),
                            )
                            .clicked()
                        {
                            self.selected_number_range_code = Some(range.code.clone());
                            self.number_range_form = NumberRangeForm::from(&range);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(520.0);
                    form_row(ui, "Muster", |ui| {
                        ui.text_edit_singleline(&mut self.number_range_form.pattern);
                    });
                    form_row(ui, "Zählerstand", |ui| {
                        ui.add(
                            egui::DragValue::new(&mut self.number_range_form.counter_value)
                                .range(0..=i64::MAX),
                        );
                    });
                    form_row(ui, "Zählerlänge", |ui| {
                        ui.add(
                            egui::DragValue::new(&mut self.number_range_form.counter_padding)
                                .range(1..=18),
                        );
                    });
                    form_row(ui, "Reset-Regel", |ui| {
                        ui.text_edit_singleline(
                            self.number_range_form
                                .reset_rule
                                .get_or_insert(String::new()),
                        );
                    });
                    ui.checkbox(&mut self.number_range_form.is_active, "Aktiv");
                    ui.label("Das Muster muss {counter} enthalten.");
                    if ui
                        .add_enabled(
                            self.selected_number_range_code.is_some(),
                            primary_action_button("Speichern"),
                        )
                        .clicked()
                    {
                        self.save_number_range();
                    }
                    ui.label(&self.number_ranges_status);
                });
            },
        );
    }

    fn customers_view(&mut self, ui: &mut egui::Ui) {
        page_header(ui, "Kunden", "Kundenstamm, Rabatt und Kontaktdaten.");
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(250.0);
                    ui.horizontal(|ui| {
                        ui.heading("Kundenliste");
                        if ui.button("Neu").clicked() {
                            self.new_customer();
                        }
                    });
                    ui.add_space(8.0);
                    ui.text_edit_singleline(&mut self.customer_list_search);
                    ui.add_space(6.0);
                    let filtered_customers: Vec<Customer> = self
                        .customers
                        .iter()
                        .filter(|customer| {
                            matches_number_or_name(
                                &customer.customer_number,
                                &customer.name,
                                &self.customer_list_search,
                            )
                        })
                        .cloned()
                        .collect();
                    for customer in filtered_customers {
                        if ui
                            .selectable_label(
                                self.selected_customer_id.as_deref() == Some(&customer.id),
                                format!("{}  {}", customer.customer_number, customer.name),
                            )
                            .clicked()
                        {
                            self.selected_customer_id = Some(customer.id.clone());
                            self.customer_form = CustomerForm::from(&customer);
                        }
                    }
                    if self.customers.is_empty() {
                        ui.label("Keine Kunden vorhanden.");
                    } else if self.customers.iter().all(|customer| {
                        !matches_number_or_name(
                            &customer.customer_number,
                            &customer.name,
                            &self.customer_list_search,
                        )
                    }) {
                        ui.label("Keine Treffer.");
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(480.0);
                    form_row(ui, "Kundennummer", |ui| {
                        ui.label(number_or_pending(&self.customer_form.customer_number));
                    });
                    form_row(ui, "Name", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.name);
                    });
                    form_row(ui, "Status", |ui| {
                        egui::ComboBox::from_id_salt("customer_status")
                            .selected_text(&self.customer_form.status)
                            .show_ui(ui, |ui| {
                                ui.selectable_value(
                                    &mut self.customer_form.status,
                                    "active".to_string(),
                                    "Aktiv",
                                );
                                ui.selectable_value(
                                    &mut self.customer_form.status,
                                    "inactive".to_string(),
                                    "Inaktiv",
                                );
                                ui.selectable_value(
                                    &mut self.customer_form.status,
                                    "blocked".to_string(),
                                    "Gesperrt",
                                );
                            });
                    });
                    form_row(ui, "Standardrabatt %", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.standard_discount_percent);
                    });
                    let terms = self.cash_discount_terms.clone();
                    form_row(ui, "Skonto-Regel", |ui| {
                        cash_discount_combo(
                            ui,
                            "customer_cash_discount",
                            &mut self.customer_form.cash_discount_term_id,
                            &terms,
                        );
                    });
                    form_row(ui, "Straße", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.street);
                    });
                    form_row(ui, "PLZ", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.postal_code);
                    });
                    form_row(ui, "Ort", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.city);
                    });
                    form_row(ui, "Land", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.country);
                    });
                    form_row(ui, "E-Mail", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.email);
                    });
                    form_row(ui, "Telefon", |ui| {
                        ui.text_edit_singleline(&mut self.customer_form.details.phone);
                    });
                    ui.horizontal(|ui| {
                        if ui
                            .add_enabled(
                                !self.customers_pending,
                                primary_action_button("Speichern"),
                            )
                            .clicked()
                        {
                            self.save_customer();
                        }
                        if ui
                            .add_enabled(
                                !self.customers_pending && self.selected_customer_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.deactivate_customer();
                        }
                    });
                    ui.add_space(8.0);
                    ui.label(egui::RichText::new(&self.customers_status).color(muted_text_color()));
                });
            },
        );
    }

    fn suppliers_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Lieferanten",
            "Lieferantenstamm und Zahlungskonditionen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(250.0);
                    if ui.button("Neuer Lieferant").clicked() {
                        self.selected_supplier_id = None;
                        self.supplier_form = SupplierForm::default();
                        self.suppliers_status = "Lieferantennummer wird reserviert.".to_string();
                        self.reserve_next_number("suppliers");
                    }
                    ui.text_edit_singleline(&mut self.supplier_list_search);
                    ui.add_space(6.0);
                    let filtered_suppliers: Vec<Supplier> = self
                        .suppliers
                        .iter()
                        .filter(|supplier| {
                            matches_number_or_name(
                                &supplier.supplier_number,
                                &supplier.name,
                                &self.supplier_list_search,
                            )
                        })
                        .cloned()
                        .collect();
                    for supplier in filtered_suppliers {
                        if ui
                            .selectable_label(
                                self.selected_supplier_id.as_deref() == Some(&supplier.id),
                                format!("{}  {}", supplier.supplier_number, supplier.name),
                            )
                            .clicked()
                        {
                            self.selected_supplier_id = Some(supplier.id.clone());
                            self.supplier_form = SupplierForm::from(&supplier);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(480.0);
                    form_row(ui, "Lieferantennummer", |ui| {
                        ui.label(number_or_pending(&self.supplier_form.supplier_number));
                    });
                    form_row(ui, "Name", |ui| {
                        ui.text_edit_singleline(&mut self.supplier_form.name);
                    });
                    form_row(ui, "Rabatt %", |ui| {
                        ui.text_edit_singleline(&mut self.supplier_form.standard_discount_percent);
                    });
                    let terms = self.cash_discount_terms.clone();
                    form_row(ui, "Skonto-Regel", |ui| {
                        cash_discount_combo(
                            ui,
                            "supplier_cash_discount",
                            &mut self.supplier_form.cash_discount_term_id,
                            &terms,
                        );
                    });
                    form_row(ui, "Zahlungsziel Tage", |ui| {
                        ui.add(
                            egui::DragValue::new(self.supplier_form.payment_days.get_or_insert(14))
                                .range(0..=365),
                        );
                    });
                    form_row(ui, "Straße", |ui| {
                        ui.text_edit_singleline(&mut self.supplier_form.details.street);
                    });
                    form_row(ui, "Ort", |ui| {
                        ui.text_edit_singleline(&mut self.supplier_form.details.city);
                    });
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_supplier();
                        }
                        if ui
                            .add_enabled(
                                self.selected_supplier_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_supplier();
                        }
                    });
                    ui.label(&self.suppliers_status);
                });
            },
        );
    }

    fn items_view(&mut self, ui: &mut egui::Ui) {
        page_header(ui, "Artikel", "Artikelstamm und Standardpreise.");
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(250.0);
                    if ui.button("Neuer Artikel").clicked() {
                        self.selected_item_id = None;
                        self.item_form = ItemForm::default();
                        self.items_status = "Artikelnummer wird reserviert.".to_string();
                        self.reserve_next_number("items");
                    }
                    ui.text_edit_singleline(&mut self.item_list_search);
                    egui::ComboBox::from_id_salt("item_sort_mode")
                        .selected_text(match self.item_sort_mode.as_str() {
                            "number_desc" => "Artikelnummer absteigend",
                            _ => "Artikelname absteigend",
                        })
                        .show_ui(ui, |ui| {
                            ui.selectable_value(
                                &mut self.item_sort_mode,
                                "name_desc".to_string(),
                                "Artikelname absteigend",
                            );
                            ui.selectable_value(
                                &mut self.item_sort_mode,
                                "number_desc".to_string(),
                                "Artikelnummer absteigend",
                            );
                        });
                    ui.add_space(6.0);
                    let mut filtered_items: Vec<Item> = self
                        .items
                        .iter()
                        .filter(|item| {
                            matches_number_or_name(
                                &item.item_number,
                                &item.name,
                                &self.item_list_search,
                            )
                        })
                        .cloned()
                        .collect();
                    if self.item_sort_mode == "number_desc" {
                        filtered_items.sort_by(|left, right| {
                            right
                                .item_number
                                .to_lowercase()
                                .cmp(&left.item_number.to_lowercase())
                        });
                    } else {
                        filtered_items.sort_by(|left, right| {
                            right.name.to_lowercase().cmp(&left.name.to_lowercase())
                        });
                    }
                    for item in filtered_items {
                        if ui
                            .selectable_label(
                                self.selected_item_id.as_deref() == Some(&item.id),
                                format!("{}  {}", item.item_number, item.name),
                            )
                            .clicked()
                        {
                            self.selected_item_id = Some(item.id.clone());
                            self.item_form = ItemForm::from(&item);
                            self.load_item_price_history(item.id);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(440.0);
                    form_row(ui, "Artikelnummer", |ui| {
                        ui.label(number_or_pending(&self.item_form.item_number));
                    });
                    form_row(ui, "Hersteller-Code", |ui| {
                        ui.text_edit_singleline(&mut self.item_form.manufacturer_code);
                    });
                    form_row(ui, "Bezeichnung", |ui| {
                        ui.text_edit_singleline(&mut self.item_form.name);
                    });
                    form_row(ui, "Einheit", |ui| {
                        unit_combo(ui, &mut self.item_form.unit);
                    });
                    form_row(ui, "Steuersatz %", |ui| {
                        tax_rate_combo(ui, &mut self.item_form.tax_rate);
                    });
                    form_row(ui, "Einkaufspreis", |ui| {
                        ui.text_edit_singleline(&mut self.item_form.default_purchase_price);
                    });
                    form_row(ui, "Verkaufspreis", |ui| {
                        ui.text_edit_singleline(&mut self.item_form.default_sales_price);
                    });
                    ui.add_space(8.0);
                    ui.horizontal(|ui| {
                        ui.heading("Lieferantenpreise");
                        if ui.button("Lieferant hinzufügen").clicked() {
                            self.item_form
                                .supplier_prices
                                .push(ItemSupplierPriceForm::default());
                        }
                    });
                    let suppliers = self.suppliers.clone();
                    let mut remove_supplier_price = None;
                    for (index, supplier_price) in
                        self.item_form.supplier_prices.iter_mut().enumerate()
                    {
                        ui.separator();
                        form_row(ui, "Lieferant", |ui| {
                            supplier_combo(
                                ui,
                                &mut supplier_price.supplier_id,
                                &suppliers,
                                &mut self.supplier_lookup_search,
                            );
                        });
                        form_row(ui, "Externe Artikelnr.", |ui| {
                            ui.text_edit_singleline(&mut supplier_price.external_item_number);
                        });
                        form_row(ui, "Einkaufspreis", |ui| {
                            ui.text_edit_singleline(&mut supplier_price.purchase_price);
                        });
                        form_row(ui, "Währung", |ui| {
                            ui.text_edit_singleline(&mut supplier_price.currency);
                        });
                        ui.checkbox(&mut supplier_price.is_preferred, "Bevorzugt");
                        if ui.button("Lieferantenpreis entfernen").clicked() {
                            remove_supplier_price = Some(index);
                        }
                    }
                    if let Some(index) = remove_supplier_price {
                        self.item_form.supplier_prices.remove(index);
                    }
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_item();
                        }
                        if ui
                            .add_enabled(
                                self.selected_item_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_item();
                        }
                    });
                    ui.label(&self.items_status);
                    ui.add_space(12.0);
                    ui.heading("Preishistorie");
                    if self.item_price_history.is_empty() {
                        ui.label("Noch keine Preisänderung vorhanden.");
                    }
                    for entry in &self.item_price_history {
                        ui.horizontal(|ui| {
                            ui.label(&entry.valid_from);
                            ui.label(format!("ID {}", &entry.id[..entry.id.len().min(8)]));
                            ui.label(format!(
                                "EK {}",
                                format_euro(entry.purchase_price.as_deref())
                            ));
                            ui.label(format!("VK {}", format_euro(entry.sales_price.as_deref())));
                            ui.label(&entry.source);
                            ui.label(format!(
                                "Item {}",
                                &entry.item_id[..entry.item_id.len().min(8)]
                            ));
                            ui.label(format!("Erfasst {}", entry.created_at));
                            if let Some(user_id) = &entry.created_by_user_id {
                                ui.label(format!("User {}", &user_id[..user_id.len().min(8)]));
                            }
                        });
                    }
                });
            },
        );
    }

    fn price_imports_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Preislistenimport",
            "CSV-Preislisten prüfen und Artikelpreise historisiert übernehmen.",
        );
        panel(ui, |ui| {
            form_row(ui, "Quelle", |ui| {
                ui.text_edit_singleline(&mut self.price_import_form.source_name);
            });
            form_row(ui, "Trennzeichen", |ui| {
                ui.text_edit_singleline(
                    self.price_import_form
                        .delimiter
                        .get_or_insert(";".to_string()),
                );
            });
            form_row(ui, "CSV-Inhalt", |ui| {
                ui.add(
                    egui::TextEdit::multiline(&mut self.price_import_form.content)
                        .desired_rows(12)
                        .desired_width(860.0),
                );
            });
            ui.horizontal(|ui| {
                if ui.button("Vorschau").clicked() {
                    self.preview_price_import();
                }
                if ui.button("Importieren").clicked() {
                    self.apply_price_import();
                }
            });
            ui.label(&self.price_import_status);
        });
        if let Some(preview) = &self.price_import_preview {
            panel(ui, |ui| {
                ui.heading("Vorschau");
                ui.label(format!(
                    "{} Zeilen, {} gültig, {} Fehler",
                    preview.total_rows, preview.valid_rows, preview.error_rows
                ));
                ui.separator();
                egui::Grid::new("price_import_preview_grid")
                    .striped(true)
                    .show(ui, |ui| {
                        ui.label("Zeile");
                        ui.label("Aktion");
                        ui.label("Artikelnummer");
                        ui.label("Name");
                        ui.label("Hersteller");
                        ui.label("Lieferant");
                        ui.label("Einheit");
                        ui.label("Steuer");
                        ui.label("EK");
                        ui.label("VK");
                        ui.label("Fehler");
                        ui.end_row();
                        for row in &preview.rows {
                            ui.label(row.row_number.to_string());
                            ui.label(&row.action);
                            ui.label(&row.item_number);
                            ui.label(&row.name);
                            ui.label(row.manufacturer_code.as_deref().unwrap_or("-"));
                            ui.label(
                                row.supplier_number
                                    .as_deref()
                                    .map(|supplier| {
                                        format!(
                                            "{} / {}",
                                            supplier,
                                            row.supplier_item_number.as_deref().unwrap_or("-")
                                        )
                                    })
                                    .unwrap_or_else(|| "-".to_string()),
                            );
                            ui.label(&row.unit);
                            ui.label(&row.tax_rate);
                            ui.label(format!(
                                "{} {}",
                                row.purchase_price.as_deref().unwrap_or("-"),
                                row.currency.as_deref().unwrap_or("EUR")
                            ));
                            ui.label(row.sales_price.as_deref().unwrap_or("-"));
                            ui.label(row.error.as_deref().unwrap_or("-"));
                            ui.end_row();
                        }
                    });
            });
        }
    }

    fn api_connectors_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Preis-APIs",
            "Externe Preisquellen konfigurieren und manuell abgleichen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(260.0);
                    if ui.button("Neue Preis-API").clicked() {
                        self.selected_api_connector_id = None;
                        self.api_connector_form = ApiConnectorForm::default();
                    }
                    for connector in self.api_connectors.clone() {
                        if ui
                            .selectable_label(
                                self.selected_api_connector_id.as_deref() == Some(&connector.id),
                                format!("{}  {}", connector.code, connector.name),
                            )
                            .clicked()
                        {
                            self.selected_api_connector_id = Some(connector.id.clone());
                            self.api_connector_form = ApiConnectorForm::from(&connector);
                        }
                        if let Some(last_sync_at) = &connector.last_sync_at {
                            ui.label(
                                egui::RichText::new(format!("zuletzt: {last_sync_at}"))
                                    .color(muted_text_color())
                                    .size(12.0),
                            );
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(560.0);
                    form_row(ui, "Code", |ui| {
                        ui.text_edit_singleline(&mut self.api_connector_form.code);
                    });
                    form_row(ui, "Name", |ui| {
                        ui.text_edit_singleline(&mut self.api_connector_form.name);
                    });
                    form_row(ui, "Typ", |ui| {
                        ui.text_edit_singleline(&mut self.api_connector_form.connector_type);
                    });
                    form_row(ui, "Intervall Minuten", |ui| {
                        ui.add(
                            egui::DragValue::new(
                                self.api_connector_form
                                    .sync_interval_minutes
                                    .get_or_insert(1440),
                            )
                            .range(1..=525_600),
                        );
                    });
                    ui.checkbox(&mut self.api_connector_form.is_active, "Aktiv");
                    form_row(ui, "Konfiguration JSON", |ui| {
                        ui.add(
                            egui::TextEdit::multiline(&mut self.api_connector_form.config)
                                .desired_rows(10)
                                .desired_width(520.0),
                        );
                    });
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_api_connector();
                        }
                        if ui
                            .add_enabled(
                                self.selected_api_connector_id.is_some(),
                                egui::Button::new("Abgleichen"),
                            )
                            .clicked()
                        {
                            self.sync_api_connector();
                        }
                        if ui
                            .add_enabled(
                                self.selected_api_connector_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_api_connector();
                        }
                    });
                    ui.label(&self.api_connectors_status);
                });
            },
        );
    }

    fn price_rules_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Preisregeln",
            "Aufschläge und Rundung je Preisquelle festlegen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(260.0);
                    if ui.button("Neue Preisregel").clicked() {
                        self.selected_price_rule_id = None;
                        self.price_rule_form = PriceRuleForm::default();
                    }
                    for rule in self.price_rules.clone() {
                        if ui
                            .selectable_label(
                                self.selected_price_rule_id.as_deref() == Some(&rule.id),
                                format!("{}  {}", rule.code, rule.name),
                            )
                            .clicked()
                        {
                            self.selected_price_rule_id = Some(rule.id.clone());
                            self.price_rule_form = PriceRuleForm::from(&rule);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(520.0);
                    form_row(ui, "Code", |ui| {
                        ui.text_edit_singleline(&mut self.price_rule_form.code);
                    });
                    form_row(ui, "Name", |ui| {
                        ui.text_edit_singleline(&mut self.price_rule_form.name);
                    });
                    form_row(ui, "Quellentyp", |ui| {
                        egui::ComboBox::from_id_salt("price_rule_source_type")
                            .selected_text(&self.price_rule_form.source_type)
                            .show_ui(ui, |ui| {
                                ui.selectable_value(
                                    &mut self.price_rule_form.source_type,
                                    "import".to_string(),
                                    "Import",
                                );
                                ui.selectable_value(
                                    &mut self.price_rule_form.source_type,
                                    "api".to_string(),
                                    "API",
                                );
                                ui.selectable_value(
                                    &mut self.price_rule_form.source_type,
                                    "supplier".to_string(),
                                    "Lieferant",
                                );
                            });
                    });
                    form_row(ui, "Quell-ID", |ui| {
                        ui.text_edit_singleline(
                            self.price_rule_form.source_id.get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Aufschlag %", |ui| {
                        ui.text_edit_singleline(&mut self.price_rule_form.markup_percent);
                    });
                    form_row(ui, "Rundung", |ui| {
                        egui::ComboBox::from_id_salt("price_rule_rounding")
                            .selected_text(&self.price_rule_form.rounding_mode)
                            .show_ui(ui, |ui| {
                                for (code, label) in [
                                    ("none", "Keine"),
                                    ("cent", "Cent"),
                                    ("five_cent", "5 Cent"),
                                    ("ten_cent", "10 Cent"),
                                    ("whole", "Ganze Beträge"),
                                ] {
                                    ui.selectable_value(
                                        &mut self.price_rule_form.rounding_mode,
                                        code.to_string(),
                                        label,
                                    );
                                }
                            });
                    });
                    ui.checkbox(&mut self.price_rule_form.is_active, "Aktiv");
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_price_rule();
                        }
                        if ui
                            .add_enabled(
                                self.selected_price_rule_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_price_rule();
                        }
                    });
                    ui.label(&self.price_rules_status);
                });
            },
        );
    }

    fn cash_discount_terms_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Skonto",
            "Zahlungsbedingungen für Kunden, Lieferanten und Belege.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(250.0);
                    if ui.button("Neue Skonto-Regel").clicked() {
                        self.selected_cash_discount_term_id = None;
                        self.cash_discount_term_form = CashDiscountTermForm::default();
                    }
                    for term in self.cash_discount_terms.clone() {
                        if ui
                            .selectable_label(
                                self.selected_cash_discount_term_id.as_deref() == Some(&term.id),
                                format!("{}  {}", term.code, term.name),
                            )
                            .clicked()
                        {
                            self.selected_cash_discount_term_id = Some(term.id.clone());
                            self.cash_discount_term_form = CashDiscountTermForm::from(&term);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(520.0);
                    form_row(ui, "Code", |ui| {
                        ui.text_edit_singleline(&mut self.cash_discount_term_form.code);
                    });
                    form_row(ui, "Name", |ui| {
                        ui.text_edit_singleline(&mut self.cash_discount_term_form.name);
                    });
                    form_row(ui, "Skonto %", |ui| {
                        ui.text_edit_singleline(&mut self.cash_discount_term_form.discount_percent);
                    });
                    form_row(ui, "Skontofrist Tage", |ui| {
                        ui.add(
                            egui::DragValue::new(&mut self.cash_discount_term_form.discount_days)
                                .range(0..=365),
                        );
                    });
                    form_row(ui, "Nettoziel Tage", |ui| {
                        ui.add(
                            egui::DragValue::new(
                                self.cash_discount_term_form.net_days.get_or_insert(30),
                            )
                            .range(0..=365),
                        );
                    });
                    form_row(ui, "Gültig ab", |ui| {
                        ui.text_edit_singleline(
                            self.cash_discount_term_form
                                .valid_from
                                .get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Gültig bis", |ui| {
                        ui.text_edit_singleline(
                            self.cash_discount_term_form
                                .valid_until
                                .get_or_insert(String::new()),
                        );
                    });
                    ui.checkbox(
                        &mut self.cash_discount_term_form.is_default_customer_term,
                        "Standard für Kunden",
                    );
                    ui.checkbox(
                        &mut self.cash_discount_term_form.is_default_supplier_term,
                        "Standard für Lieferanten",
                    );
                    ui.checkbox(&mut self.cash_discount_term_form.is_active, "Aktiv");
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_cash_discount_term();
                        }
                        if ui
                            .add_enabled(
                                self.selected_cash_discount_term_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_cash_discount_term();
                        }
                    });
                    ui.label(&self.cash_discount_terms_status);
                });
            },
        );
    }

    fn quotes_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Angebote",
            "Angebote mit festen Artikelpositionen und individuellen Preisen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(300.0);
                    if ui.button("Neues Angebot").clicked() {
                        self.selected_quote_id = None;
                        self.quote_form = QuoteForm::default();
                        self.quotes_status = "Angebotsnummer wird reserviert.".to_string();
                        self.reserve_next_number("quotes");
                    }
                    ui.text_edit_singleline(&mut self.quote_list_search);
                    ui.add_space(6.0);
                    let quote_query = self.quote_list_search.trim().to_lowercase();
                    let filtered_quotes: Vec<Quote> = self
                        .quotes
                        .iter()
                        .filter(|quote| {
                            let customer = self
                                .customers
                                .iter()
                                .find(|customer| customer.id == quote.customer_id)
                                .map(|customer| customer.name.as_str())
                                .unwrap_or("-");
                            quote_query.is_empty()
                                || format!("{} {} {}", quote.quote_number, customer, quote.status)
                                    .to_lowercase()
                                    .contains(&quote_query)
                        })
                        .cloned()
                        .collect();
                    for quote in filtered_quotes {
                        let customer = self
                            .customers
                            .iter()
                            .find(|customer| customer.id == quote.customer_id)
                            .map(|customer| customer.name.as_str())
                            .unwrap_or("-");
                        if ui
                            .selectable_label(
                                self.selected_quote_id.as_deref() == Some(&quote.id),
                                format!("{}  {}  {}", quote.quote_number, customer, quote.status),
                            )
                            .clicked()
                        {
                            self.selected_quote_id = Some(quote.id.clone());
                            self.quote_form = QuoteForm::from(&quote);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(680.0);
                    form_row(ui, "Angebotsnummer", |ui| {
                        ui.label(number_or_pending(&self.quote_form.quote_number));
                    });
                    form_row(ui, "Kunde", |ui| {
                        let previous_customer_id = self.quote_form.customer_id.clone();
                        customer_combo(
                            ui,
                            &mut self.quote_form.customer_id,
                            &self.customers,
                            &mut self.customer_lookup_search,
                        );
                        if self.quote_form.customer_id != previous_customer_id {
                            apply_quote_customer_defaults(&mut self.quote_form, &self.customers);
                        }
                    });
                    form_row(ui, "Status", |ui| {
                        egui::ComboBox::from_id_salt("quote_status")
                            .selected_text(&self.quote_form.status)
                            .show_ui(ui, |ui| {
                                for (code, label) in [
                                    ("draft", "Entwurf"),
                                    ("sent", "Gesendet"),
                                    ("accepted", "Angenommen"),
                                    ("rejected", "Abgelehnt"),
                                    ("expired", "Abgelaufen"),
                                    ("cancelled", "Storniert"),
                                ] {
                                    ui.selectable_value(
                                        &mut self.quote_form.status,
                                        code.to_string(),
                                        label,
                                    );
                                }
                            });
                    });
                    form_row(ui, "Gültig bis", |ui| {
                        ui.text_edit_singleline(
                            self.quote_form.valid_until.get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Kundenrabatt %", |ui| {
                        ui.text_edit_singleline(&mut self.quote_form.customer_discount_percent);
                    });
                    form_row(ui, "Notizen", |ui| {
                        ui.text_edit_multiline(&mut self.quote_form.notes);
                    });
                    ui.add_space(8.0);
                    ui.horizontal(|ui| {
                        ui.heading("Positionen");
                        if ui.button("Position hinzufügen").clicked() {
                            self.quote_form.items.push(QuoteItemForm::default());
                        }
                    });
                    let mut remove_index = None;
                    let items = self.items.clone();
                    let can_remove_quote_item = self.quote_form.items.len() > 1;
                    for (index, line) in self.quote_form.items.iter_mut().enumerate() {
                        ui.separator();
                        ui.label(format!("Position {}", index + 1));
                        form_row(ui, "Artikel", |ui| {
                            item_combo(ui, &mut line.item_id, &items, &mut self.item_lookup_search);
                        });
                        if ui.button("Artikelwerte übernehmen").clicked() {
                            apply_quote_item_defaults(line, &items);
                        }
                        form_row(ui, "Beschreibung", |ui| {
                            ui.text_edit_singleline(&mut line.description);
                        });
                        egui::Grid::new(format!("quote_item_fields_{index}"))
                            .num_columns(2)
                            .spacing([14.0, 8.0])
                            .show(ui, |ui| {
                                ui.label("Menge");
                                ui.text_edit_singleline(&mut line.quantity);
                                ui.end_row();
                                ui.label("Preis");
                                ui.text_edit_singleline(&mut line.unit_price);
                                ui.end_row();
                                ui.label("Original");
                                ui.text_edit_singleline(
                                    line.original_unit_price.get_or_insert(String::new()),
                                );
                                ui.end_row();
                                ui.label("Rabatt %");
                                ui.text_edit_singleline(&mut line.discount_percent);
                                ui.end_row();
                                ui.label("Steuer %");
                                tax_rate_combo(ui, &mut line.tax_rate);
                                ui.end_row();
                            });
                        if can_remove_quote_item && ui.button("Position entfernen").clicked() {
                            remove_index = Some(index);
                        }
                    }
                    if let Some(index) = remove_index {
                        self.quote_form.items.remove(index);
                    }
                    ui.add_space(10.0);
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_quote();
                        }
                        if ui
                            .add_enabled(
                                self.selected_quote_id.is_some(),
                                egui::Button::new("Stornieren"),
                            )
                            .clicked()
                        {
                            self.delete_quote();
                        }
                    });
                    ui.label(&self.quotes_status);
                });
            },
        );
    }

    fn outgoing_invoices_view(&mut self, ui: &mut egui::Ui) {
        page_header(
            ui,
            "Rechnungen",
            "Ausgangsrechnungen erstellen und abschließen.",
        );
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(300.0);
                    if ui.button("Neue Rechnung").clicked() {
                        self.selected_outgoing_invoice_id = None;
                        self.outgoing_invoice_form = OutgoingInvoiceForm::default();
                        self.outgoing_invoices_status =
                            "Rechnungsnummer wird reserviert.".to_string();
                        self.reserve_next_number("outgoing_invoices");
                    }
                    ui.text_edit_singleline(&mut self.outgoing_invoice_list_search);
                    ui.add_space(6.0);
                    let invoice_query = self.outgoing_invoice_list_search.trim().to_lowercase();
                    let filtered_invoices: Vec<OutgoingInvoice> = self
                        .outgoing_invoices
                        .iter()
                        .filter(|invoice| {
                            let customer = self
                                .customers
                                .iter()
                                .find(|customer| customer.id == invoice.customer_id)
                                .map(|customer| customer.name.as_str())
                                .unwrap_or("-");
                            invoice_query.is_empty()
                                || format!(
                                    "{} {} {}",
                                    invoice.invoice_number, customer, invoice.status
                                )
                                .to_lowercase()
                                .contains(&invoice_query)
                        })
                        .cloned()
                        .collect();
                    for invoice in filtered_invoices {
                        let customer = self
                            .customers
                            .iter()
                            .find(|customer| customer.id == invoice.customer_id)
                            .map(|customer| customer.name.as_str())
                            .unwrap_or("-");
                        if ui
                            .selectable_label(
                                self.selected_outgoing_invoice_id.as_deref() == Some(&invoice.id),
                                format!(
                                    "{}  {}  {}{}",
                                    invoice.invoice_number,
                                    customer,
                                    invoice.status,
                                    invoice
                                        .finalized_at
                                        .as_deref()
                                        .map(|_| "  abgeschlossen")
                                        .unwrap_or("")
                                ),
                            )
                            .clicked()
                        {
                            self.selected_outgoing_invoice_id = Some(invoice.id.clone());
                            self.outgoing_invoice_form = OutgoingInvoiceForm::from(&invoice);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(680.0);
                    form_row(ui, "Rechnungsnummer", |ui| {
                        ui.label(number_or_pending(
                            &self.outgoing_invoice_form.invoice_number,
                        ));
                    });
                    form_row(ui, "Kunde", |ui| {
                        let previous_customer_id =
                            self.outgoing_invoice_form.customer_id.clone();
                        customer_combo(
                            ui,
                            &mut self.outgoing_invoice_form.customer_id,
                            &self.customers,
                            &mut self.customer_lookup_search,
                        );
                        if self.outgoing_invoice_form.customer_id != previous_customer_id {
                            apply_outgoing_invoice_customer_defaults(
                                &mut self.outgoing_invoice_form,
                                &self.customers,
                            );
                        }
                    });
                    form_row(ui, "Status", |ui| {
                        ui.text_edit_singleline(&mut self.outgoing_invoice_form.status);
                    });
                    form_row(ui, "Ausgestellt", |ui| {
                        ui.text_edit_singleline(
                            self.outgoing_invoice_form
                                .issued_at
                                .get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Fällig", |ui| {
                        ui.text_edit_singleline(
                            self.outgoing_invoice_form
                                .due_at
                                .get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Kundenrabatt %", |ui| {
                        ui.text_edit_singleline(
                            &mut self.outgoing_invoice_form.customer_discount_percent,
                        );
                    });
                    invoice_items_editor(
                        ui,
                        &mut self.outgoing_invoice_form.items,
                        &self.items,
                        &mut self.item_lookup_search,
                    );
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_outgoing_invoice();
                        }
                        if ui
                            .add_enabled(
                                self.selected_outgoing_invoice_id.is_some(),
                                egui::Button::new("Abschließen"),
                            )
                            .clicked()
                        {
                            self.finalize_outgoing_invoice();
                        }
                        if ui
                            .add_enabled(
                                self.selected_outgoing_invoice_id.is_some(),
                                egui::Button::new("Stornieren"),
                            )
                            .clicked()
                        {
                            self.delete_outgoing_invoice();
                        }
                    });
                    ui.label(&self.outgoing_invoices_status);
                });
            },
        );
    }

    fn incoming_invoices_view(&mut self, ui: &mut egui::Ui) {
        page_header(ui, "Eingangsrechnungen", "Lieferantenrechnungen erfassen.");
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(300.0);
                    if ui.button("Neue Eingangsrechnung").clicked() {
                        self.selected_incoming_invoice_id = None;
                        self.incoming_invoice_form = IncomingInvoiceForm::default();
                        self.incoming_invoices_status =
                            "Eingangsrechnungsnummer wird reserviert.".to_string();
                        self.reserve_next_number("incoming_invoices");
                    }
                    ui.text_edit_singleline(&mut self.incoming_invoice_list_search);
                    ui.add_space(6.0);
                    let invoice_query = self.incoming_invoice_list_search.trim().to_lowercase();
                    let filtered_invoices: Vec<IncomingInvoice> = self
                        .incoming_invoices
                        .iter()
                        .filter(|invoice| {
                            let supplier = self
                                .suppliers
                                .iter()
                                .find(|supplier| supplier.id == invoice.supplier_id)
                                .map(|supplier| supplier.name.as_str())
                                .unwrap_or("-");
                            invoice_query.is_empty()
                                || format!(
                                    "{} {} {}",
                                    invoice.invoice_number, supplier, invoice.status
                                )
                                .to_lowercase()
                                .contains(&invoice_query)
                        })
                        .cloned()
                        .collect();
                    for invoice in filtered_invoices {
                        let supplier = self
                            .suppliers
                            .iter()
                            .find(|supplier| supplier.id == invoice.supplier_id)
                            .map(|supplier| supplier.name.as_str())
                            .unwrap_or("-");
                        if ui
                            .selectable_label(
                                self.selected_incoming_invoice_id.as_deref() == Some(&invoice.id),
                                format!(
                                    "{}  {}  {}",
                                    invoice.invoice_number, supplier, invoice.status
                                ),
                            )
                            .clicked()
                        {
                            self.selected_incoming_invoice_id = Some(invoice.id.clone());
                            self.incoming_invoice_form = IncomingInvoiceForm::from(&invoice);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(680.0);
                    form_row(ui, "Rechnungsnummer", |ui| {
                        ui.label(number_or_pending(
                            &self.incoming_invoice_form.invoice_number,
                        ));
                    });
                    form_row(ui, "Lieferant", |ui| {
                        supplier_combo(
                            ui,
                            &mut self.incoming_invoice_form.supplier_id,
                            &self.suppliers,
                            &mut self.supplier_lookup_search,
                        );
                    });
                    form_row(ui, "Status", |ui| {
                        ui.text_edit_singleline(&mut self.incoming_invoice_form.status);
                    });
                    form_row(ui, "Datum", |ui| {
                        ui.text_edit_singleline(
                            self.incoming_invoice_form
                                .invoice_date
                                .get_or_insert(String::new()),
                        );
                    });
                    form_row(ui, "Fällig", |ui| {
                        ui.text_edit_singleline(
                            self.incoming_invoice_form
                                .due_at
                                .get_or_insert(String::new()),
                        );
                    });
                    incoming_invoice_items_editor(
                        ui,
                        &mut self.incoming_invoice_form.items,
                        &self.items,
                        &mut self.item_lookup_search,
                    );
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_incoming_invoice();
                        }
                        if ui
                            .add_enabled(
                                self.selected_incoming_invoice_id.is_some(),
                                egui::Button::new("Stornieren"),
                            )
                            .clicked()
                        {
                            self.delete_incoming_invoice();
                        }
                    });
                    ui.label(&self.incoming_invoices_status);
                });
            },
        );
    }

    fn activities_view(&mut self, ui: &mut egui::Ui) {
        page_header(ui, "Aktivitäten", "Aufgaben und Notizen.");
        master_detail!(
            ui,
            {
                panel(ui, |ui| {
                    ui.set_min_width(250.0);
                    if ui.button("Neue Aktivität").clicked() {
                        self.selected_activity_id = None;
                        self.activity_form = ActivityForm::default();
                        self.activities_status = "Aktivitätsnummer wird reserviert.".to_string();
                        self.reserve_next_number("activities");
                    }
                    ui.text_edit_singleline(&mut self.activity_list_search);
                    ui.add_space(6.0);
                    let filtered_activities: Vec<Activity> = self
                        .activities
                        .iter()
                        .filter(|record| {
                            matches_number_or_name(
                                record
                                    .activity_number
                                    .as_deref()
                                    .unwrap_or(&record.activity_type),
                                &record.title,
                                &self.activity_list_search,
                            )
                        })
                        .cloned()
                        .collect();
                    for record in filtered_activities {
                        if ui
                            .selectable_label(
                                self.selected_activity_id.as_deref() == Some(&record.id),
                                format!(
                                    "{}  {}",
                                    record
                                        .activity_number
                                        .as_deref()
                                        .unwrap_or(&record.activity_type),
                                    record.title
                                ),
                            )
                            .clicked()
                        {
                            self.selected_activity_id = Some(record.id.clone());
                            self.activity_form = ActivityForm::from(&record);
                        }
                    }
                });
            },
            {
                panel(ui, |ui| {
                    ui.set_min_width(450.0);
                    form_row(ui, "Titel", |ui| {
                        ui.text_edit_singleline(&mut self.activity_form.title);
                    });
                    form_row(ui, "Aktivitätsnummer", |ui| {
                        ui.label(number_or_pending(
                            self.activity_form.activity_number.as_deref().unwrap_or(""),
                        ));
                    });
                    form_row(ui, "Typ", |ui| {
                        ui.text_edit_singleline(&mut self.activity_form.activity_type);
                    });
                    form_row(ui, "Status", |ui| {
                        egui::ComboBox::from_id_salt("activity_status")
                            .selected_text(match self.activity_form.status.as_str() {
                                "inactive" => "Nicht aktiv",
                                _ => "Aktiv",
                            })
                            .show_ui(ui, |ui| {
                                ui.selectable_value(
                                    &mut self.activity_form.status,
                                    "active".to_string(),
                                    "Aktiv",
                                );
                                ui.selectable_value(
                                    &mut self.activity_form.status,
                                    "inactive".to_string(),
                                    "Nicht aktiv",
                                );
                            });
                    });
                    form_row(ui, "Priorität", |ui| {
                        ui.text_edit_singleline(&mut self.activity_form.priority);
                    });
                    form_row(ui, "Beschreibung", |ui| {
                        ui.text_edit_multiline(&mut self.activity_form.body);
                    });
                    ui.horizontal(|ui| {
                        if ui.button("Speichern").clicked() {
                            self.save_activity();
                        }
                        if ui
                            .add_enabled(
                                self.selected_activity_id.is_some(),
                                egui::Button::new("Deaktivieren"),
                            )
                            .clicked()
                        {
                            self.delete_activity();
                        }
                    });
                    ui.label(&self.activities_status);
                });
            },
        );
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AppView {
    Login,
    Status,
    Register,
}

type RegistrationResult = Result<RegistrationResponse, String>;
type LoginResult = Result<LoginResponse, String>;
type DevBootstrapResult = Result<DevBootstrapResponse, String>;
type UsersResult = Result<Vec<OrganizationUser>, String>;
type RegistrationsResult = Result<Vec<OrganizationRegistration>, String>;
type NumberRangesResult = Result<Vec<NumberRange>, String>;
type CustomersResult = Result<Vec<Customer>, String>;
type SuppliersResult = Result<Vec<Supplier>, String>;
type CashDiscountTermsResult = Result<Vec<CashDiscountTerm>, String>;
type ItemsResult = Result<Vec<Item>, String>;
type ItemPriceHistoryResult = Result<Vec<ItemPriceHistory>, String>;
type QuotesResult = Result<Vec<Quote>, String>;
type OutgoingInvoicesResult = Result<Vec<OutgoingInvoice>, String>;
type IncomingInvoicesResult = Result<Vec<IncomingInvoice>, String>;
type ActivitiesResult = Result<Vec<Activity>, String>;
type ApiConnectorsResult = Result<Vec<ApiConnector>, String>;
type PriceRulesResult = Result<Vec<PriceRule>, String>;
type EmptyResult = Result<(), String>;

#[derive(Debug, Clone)]
enum UserAdminEvent {
    UsersLoaded(UsersResult),
    InvitationSent(Result<String, String>),
    RolesSaved {
        user_id: String,
        result: EmptyResult,
    },
}

#[derive(Debug, Clone)]
enum AdminEvent {
    NavigationSettingsLoaded(Result<UserNavigationSettings, String>),
    NavigationSettingsSaved(Result<UserNavigationSettings, String>),
    OrganizationSetupSaved(EmptyResult),
    OrganizationSetupLoaded(Result<Option<OrganizationSetupForm>, String>),
    RegistrationsLoaded(RegistrationsResult),
    RegistrationActionDone(Result<String, String>),
    NumberRangesLoaded(NumberRangesResult),
    NumberRangeSaved(Result<NumberRange, String>),
    NextNumberReserved {
        code: String,
        result: Result<String, String>,
    },
    CustomersLoaded(CustomersResult),
    CustomerSaved(Result<Customer, String>),
    CustomerDeleted(EmptyResult),
    SuppliersLoaded(SuppliersResult),
    SupplierSaved(Result<Supplier, String>),
    SupplierDeleted(EmptyResult),
    CashDiscountTermsLoaded(CashDiscountTermsResult),
    CashDiscountTermSaved(Result<CashDiscountTerm, String>),
    CashDiscountTermDeleted(EmptyResult),
    ItemsLoaded(ItemsResult),
    ItemSaved(Result<Item, String>),
    ItemDeleted(EmptyResult),
    ItemPriceHistoryLoaded(ItemPriceHistoryResult),
    QuotesLoaded(QuotesResult),
    QuoteSaved(Result<Quote, String>),
    QuoteDeleted(EmptyResult),
    OutgoingInvoicesLoaded(OutgoingInvoicesResult),
    OutgoingInvoiceSaved(Result<OutgoingInvoice, String>),
    OutgoingInvoiceDeleted(EmptyResult),
    OutgoingInvoiceFinalized(EmptyResult),
    IncomingInvoicesLoaded(IncomingInvoicesResult),
    IncomingInvoiceSaved(Result<IncomingInvoice, String>),
    IncomingInvoiceDeleted(EmptyResult),
    ActivitiesLoaded(ActivitiesResult),
    ActivitySaved(Result<Activity, String>),
    ActivityDeleted(EmptyResult),
    PriceImportPreviewed(Result<PriceListImportPreview, String>),
    PriceImportApplied(Result<PriceListImportApplyResponse, String>),
    ApiConnectorsLoaded(ApiConnectorsResult),
    ApiConnectorSaved(Result<ApiConnector, String>),
    ApiConnectorDeleted(EmptyResult),
    ApiConnectorSynced(Result<ApiConnectorSyncResponse, String>),
    PriceRulesLoaded(PriceRulesResult),
    PriceRuleSaved(Result<PriceRule, String>),
    PriceRuleDeleted(EmptyResult),
}

#[derive(Debug, Clone, Deserialize)]
struct LoginResponse {
    user_id: String,
    access_token: String,
    organization_id: Option<String>,
    must_change_password: bool,
    organizations: Vec<LoginOrganization>,
}

#[derive(Debug, Clone, Deserialize)]
struct LoginOrganization {
    id: String,
    schema_name: Option<String>,
    status: String,
}

#[derive(Debug, Serialize)]
struct LoginRequest {
    email: String,
    password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct UserNavigationSettings {
    mode: NavigationMode,
}

#[derive(Debug, Clone, Deserialize)]
struct DevBootstrapResponse {
    organization_id: String,
    schema_name: String,
    user_id: String,
    email: String,
    password: String,
    dev_mode: bool,
}

#[derive(Debug, Serialize)]
struct DevBootstrapRequest {
    organization_name: String,
    email: String,
}

#[derive(Debug, Clone, Deserialize)]
struct RegistrationResponse {
    id: String,
    status: String,
}

#[derive(Debug, Serialize)]
struct RegistrationRequest {
    organization_name: String,
    email: String,
    accept_terms: bool,
}

#[derive(Debug, Clone, Deserialize)]
struct OrganizationUser {
    user_id: String,
    email: String,
    status: String,
    roles: Vec<String>,
}

#[derive(Debug, Serialize)]
struct InviteUserRequest {
    email: String,
    roles: Vec<String>,
}

#[derive(Debug, Serialize)]
struct UpdateUserRolesRequest {
    roles: Vec<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
struct OrganizationSetupForm {
    display_name: String,
    legal_form: String,
    street: String,
    postal_code: String,
    city: String,
    country: String,
    vat_id: String,
    email: String,
    phone: String,
    default_tax_rate: String,
    default_payment_days: String,
}

#[derive(Debug, Clone, Deserialize)]
struct OrganizationSetupResponse {
    setup: Option<OrganizationSetupForm>,
}

impl Default for OrganizationSetupForm {
    fn default() -> Self {
        Self {
            display_name: String::new(),
            legal_form: String::new(),
            street: String::new(),
            postal_code: String::new(),
            city: String::new(),
            country: "Deutschland".to_string(),
            vat_id: String::new(),
            email: String::new(),
            phone: String::new(),
            default_tax_rate: "19".to_string(),
            default_payment_days: "14".to_string(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct OrganizationRegistration {
    id: String,
    organization_name: String,
    email: String,
    status: String,
    requested_at: String,
}

#[derive(Debug, Clone, Deserialize)]
struct ApproveRegistrationResponse {
    dev_initial_password: String,
}

#[derive(Debug, Clone, Deserialize)]
struct NumberRange {
    id: String,
    code: String,
    pattern: String,
    counter_value: i64,
    counter_padding: i32,
    reset_rule: Option<String>,
    is_active: bool,
}

#[derive(Debug, Clone, Deserialize)]
struct NextNumberResponse {
    number: String,
}

#[derive(Debug, Clone, Serialize)]
struct NumberRangeForm {
    pattern: String,
    counter_value: i64,
    counter_padding: i32,
    reset_rule: Option<String>,
    is_active: bool,
}

impl Default for NumberRangeForm {
    fn default() -> Self {
        Self {
            pattern: "{counter}".to_string(),
            counter_value: 0,
            counter_padding: 9,
            reset_rule: None,
            is_active: true,
        }
    }
}

impl From<&NumberRange> for NumberRangeForm {
    fn from(value: &NumberRange) -> Self {
        Self {
            pattern: value.pattern.clone(),
            counter_value: value.counter_value,
            counter_padding: value.counter_padding,
            reset_rule: value.reset_rule.clone(),
            is_active: value.is_active,
        }
    }
}

#[derive(Debug, Clone, Default, Deserialize, Serialize)]
struct CustomerDetails {
    street: String,
    postal_code: String,
    city: String,
    country: String,
    email: String,
    phone: String,
}

#[derive(Debug, Clone, Deserialize)]
struct Customer {
    id: String,
    customer_number: String,
    name: String,
    status: String,
    details: CustomerDetails,
    standard_discount_percent: String,
    cash_discount_term_id: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
struct CustomerForm {
    customer_number: String,
    name: String,
    status: String,
    details: CustomerDetails,
    standard_discount_percent: String,
    cash_discount_term_id: Option<String>,
}

impl Default for CustomerForm {
    fn default() -> Self {
        Self {
            customer_number: String::new(),
            name: String::new(),
            status: "active".to_string(),
            details: CustomerDetails {
                country: "Deutschland".to_string(),
                ..Default::default()
            },
            standard_discount_percent: "0".to_string(),
            cash_discount_term_id: None,
        }
    }
}

impl From<&Customer> for CustomerForm {
    fn from(customer: &Customer) -> Self {
        Self {
            customer_number: customer.customer_number.clone(),
            name: customer.name.clone(),
            status: customer.status.clone(),
            details: customer.details.clone(),
            standard_discount_percent: customer.standard_discount_percent.clone(),
            cash_discount_term_id: customer.cash_discount_term_id.clone(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct Supplier {
    id: String,
    supplier_number: String,
    name: String,
    status: String,
    details: CustomerDetails,
    standard_discount_percent: String,
    cash_discount_term_id: Option<String>,
    payment_days: Option<i32>,
}

#[derive(Debug, Clone, Serialize)]
struct SupplierForm {
    supplier_number: String,
    name: String,
    status: String,
    details: CustomerDetails,
    standard_discount_percent: String,
    cash_discount_term_id: Option<String>,
    payment_days: Option<i32>,
}

impl Default for SupplierForm {
    fn default() -> Self {
        Self {
            supplier_number: String::new(),
            name: String::new(),
            status: "active".to_string(),
            details: CustomerDetails {
                country: "Deutschland".to_string(),
                ..Default::default()
            },
            standard_discount_percent: "0".to_string(),
            cash_discount_term_id: None,
            payment_days: Some(14),
        }
    }
}
impl From<&Supplier> for SupplierForm {
    fn from(value: &Supplier) -> Self {
        Self {
            supplier_number: value.supplier_number.clone(),
            name: value.name.clone(),
            status: value.status.clone(),
            details: value.details.clone(),
            standard_discount_percent: value.standard_discount_percent.clone(),
            cash_discount_term_id: value.cash_discount_term_id.clone(),
            payment_days: value.payment_days,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct CashDiscountTerm {
    id: String,
    code: String,
    name: String,
    discount_percent: String,
    discount_days: i32,
    net_days: Option<i32>,
    valid_from: Option<String>,
    valid_until: Option<String>,
    is_default_customer_term: bool,
    is_default_supplier_term: bool,
    is_active: bool,
}

#[derive(Debug, Clone, Serialize)]
struct CashDiscountTermForm {
    code: String,
    name: String,
    discount_percent: String,
    discount_days: i32,
    net_days: Option<i32>,
    valid_from: Option<String>,
    valid_until: Option<String>,
    is_default_customer_term: bool,
    is_default_supplier_term: bool,
    is_active: bool,
}

impl Default for CashDiscountTermForm {
    fn default() -> Self {
        Self {
            code: String::new(),
            name: String::new(),
            discount_percent: "2".to_string(),
            discount_days: 10,
            net_days: Some(30),
            valid_from: None,
            valid_until: None,
            is_default_customer_term: false,
            is_default_supplier_term: false,
            is_active: true,
        }
    }
}

impl From<&CashDiscountTerm> for CashDiscountTermForm {
    fn from(value: &CashDiscountTerm) -> Self {
        Self {
            code: value.code.clone(),
            name: value.name.clone(),
            discount_percent: value.discount_percent.clone(),
            discount_days: value.discount_days,
            net_days: value.net_days,
            valid_from: value.valid_from.clone(),
            valid_until: value.valid_until.clone(),
            is_default_customer_term: value.is_default_customer_term,
            is_default_supplier_term: value.is_default_supplier_term,
            is_active: value.is_active,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct Item {
    id: String,
    item_number: String,
    name: String,
    manufacturer_code: Option<String>,
    unit: String,
    tax_rate: String,
    default_purchase_price: Option<String>,
    default_sales_price: Option<String>,
    status: String,
    supplier_prices: Vec<ItemSupplierPrice>,
}

#[derive(Debug, Clone, Deserialize)]
struct ItemSupplierPrice {
    supplier_id: String,
    external_item_number: String,
    purchase_price: String,
    currency: String,
    is_preferred: bool,
}

#[derive(Debug, Clone, Deserialize)]
struct ItemPriceHistory {
    id: String,
    item_id: String,
    purchase_price: Option<String>,
    sales_price: Option<String>,
    source: String,
    valid_from: String,
    created_by_user_id: Option<String>,
    created_at: String,
}
#[derive(Debug, Clone, Serialize)]
struct ItemForm {
    item_number: String,
    name: String,
    manufacturer_code: String,
    unit: String,
    tax_rate: String,
    default_purchase_price: String,
    default_sales_price: String,
    status: String,
    supplier_prices: Vec<ItemSupplierPriceForm>,
}

#[derive(Debug, Clone, Serialize)]
struct ItemSupplierPriceForm {
    supplier_id: String,
    external_item_number: String,
    purchase_price: String,
    currency: String,
    is_preferred: bool,
}
impl Default for ItemForm {
    fn default() -> Self {
        Self {
            item_number: String::new(),
            name: String::new(),
            manufacturer_code: String::new(),
            unit: "Stck".to_string(),
            tax_rate: "19".to_string(),
            default_purchase_price: "0".to_string(),
            default_sales_price: "0".to_string(),
            status: "active".to_string(),
            supplier_prices: Vec::new(),
        }
    }
}
impl From<&Item> for ItemForm {
    fn from(value: &Item) -> Self {
        Self {
            item_number: value.item_number.clone(),
            name: value.name.clone(),
            manufacturer_code: value.manufacturer_code.clone().unwrap_or_default(),
            unit: value.unit.clone(),
            tax_rate: normalize_tax_rate(&value.tax_rate),
            default_purchase_price: value.default_purchase_price.clone().unwrap_or_default(),
            default_sales_price: value.default_sales_price.clone().unwrap_or_default(),
            status: value.status.clone(),
            supplier_prices: value
                .supplier_prices
                .iter()
                .map(ItemSupplierPriceForm::from)
                .collect(),
        }
    }
}

impl Default for ItemSupplierPriceForm {
    fn default() -> Self {
        Self {
            supplier_id: String::new(),
            external_item_number: String::new(),
            purchase_price: "0".to_string(),
            currency: "EUR".to_string(),
            is_preferred: false,
        }
    }
}

impl From<&ItemSupplierPrice> for ItemSupplierPriceForm {
    fn from(value: &ItemSupplierPrice) -> Self {
        Self {
            supplier_id: value.supplier_id.clone(),
            external_item_number: value.external_item_number.clone(),
            purchase_price: value.purchase_price.clone(),
            currency: value.currency.clone(),
            is_preferred: value.is_preferred,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct Quote {
    id: String,
    quote_number: String,
    customer_id: String,
    status: String,
    valid_until: Option<String>,
    cash_discount_term_id: Option<String>,
    customer_discount_percent: String,
    notes: String,
    items: Vec<QuoteItem>,
}

#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
struct QuoteItem {
    id: String,
    line_number: i32,
    item_id: String,
    description: String,
    quantity: String,
    unit_price: String,
    original_unit_price: Option<String>,
    discount_percent: String,
    tax_rate: String,
    price_overridden: bool,
}

#[derive(Debug, Clone, Serialize)]
struct QuoteForm {
    quote_number: String,
    customer_id: String,
    status: String,
    valid_until: Option<String>,
    cash_discount_term_id: Option<String>,
    customer_discount_percent: String,
    notes: String,
    items: Vec<QuoteItemForm>,
}

#[derive(Debug, Clone, Serialize)]
struct QuoteItemForm {
    item_id: String,
    description: String,
    quantity: String,
    unit_price: String,
    original_unit_price: Option<String>,
    discount_percent: String,
    tax_rate: String,
}

impl Default for QuoteForm {
    fn default() -> Self {
        Self {
            quote_number: String::new(),
            customer_id: String::new(),
            status: "draft".to_string(),
            valid_until: None,
            cash_discount_term_id: None,
            customer_discount_percent: "0".to_string(),
            notes: String::new(),
            items: vec![QuoteItemForm::default()],
        }
    }
}

impl Default for QuoteItemForm {
    fn default() -> Self {
        Self {
            item_id: String::new(),
            description: String::new(),
            quantity: "1".to_string(),
            unit_price: "0".to_string(),
            original_unit_price: None,
            discount_percent: "0".to_string(),
            tax_rate: "19".to_string(),
        }
    }
}

impl From<&Quote> for QuoteForm {
    fn from(value: &Quote) -> Self {
        Self {
            quote_number: value.quote_number.clone(),
            customer_id: value.customer_id.clone(),
            status: value.status.clone(),
            valid_until: value.valid_until.clone(),
            cash_discount_term_id: value.cash_discount_term_id.clone(),
            customer_discount_percent: value.customer_discount_percent.clone(),
            notes: value.notes.clone(),
            items: value.items.iter().map(QuoteItemForm::from).collect(),
        }
    }
}

impl From<&QuoteItem> for QuoteItemForm {
    fn from(value: &QuoteItem) -> Self {
        Self {
            item_id: value.item_id.clone(),
            description: value.description.clone(),
            quantity: value.quantity.clone(),
            unit_price: value.unit_price.clone(),
            original_unit_price: value.original_unit_price.clone(),
            discount_percent: value.discount_percent.clone(),
            tax_rate: normalize_tax_rate(&value.tax_rate),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct OutgoingInvoice {
    id: String,
    invoice_number: String,
    customer_id: String,
    status: String,
    cash_discount_term_id: Option<String>,
    customer_discount_percent: String,
    issued_at: Option<String>,
    due_at: Option<String>,
    source_quote_id: Option<String>,
    finalized_at: Option<String>,
    items: Vec<OutgoingInvoiceItem>,
}

#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
struct OutgoingInvoiceItem {
    id: String,
    line_number: i32,
    item_id: String,
    description: String,
    quantity: String,
    unit_price: String,
    original_unit_price: Option<String>,
    discount_percent: String,
    tax_rate: String,
    price_overridden: bool,
}

#[derive(Debug, Clone, Serialize)]
struct OutgoingInvoiceForm {
    invoice_number: String,
    customer_id: String,
    status: String,
    cash_discount_term_id: Option<String>,
    customer_discount_percent: String,
    issued_at: Option<String>,
    due_at: Option<String>,
    source_quote_id: Option<String>,
    items: Vec<OutgoingInvoiceItemForm>,
}

#[derive(Debug, Clone, Serialize)]
struct OutgoingInvoiceItemForm {
    item_id: String,
    description: String,
    quantity: String,
    unit_price: String,
    original_unit_price: Option<String>,
    discount_percent: String,
    tax_rate: String,
}

impl Default for OutgoingInvoiceForm {
    fn default() -> Self {
        Self {
            invoice_number: String::new(),
            customer_id: String::new(),
            status: "draft".to_string(),
            cash_discount_term_id: None,
            customer_discount_percent: "0".to_string(),
            issued_at: None,
            due_at: None,
            source_quote_id: None,
            items: vec![OutgoingInvoiceItemForm::default()],
        }
    }
}

impl Default for OutgoingInvoiceItemForm {
    fn default() -> Self {
        Self {
            item_id: String::new(),
            description: String::new(),
            quantity: "1".to_string(),
            unit_price: "0".to_string(),
            original_unit_price: None,
            discount_percent: "0".to_string(),
            tax_rate: "19".to_string(),
        }
    }
}

impl From<&OutgoingInvoice> for OutgoingInvoiceForm {
    fn from(value: &OutgoingInvoice) -> Self {
        Self {
            invoice_number: value.invoice_number.clone(),
            customer_id: value.customer_id.clone(),
            status: value.status.clone(),
            cash_discount_term_id: value.cash_discount_term_id.clone(),
            customer_discount_percent: value.customer_discount_percent.clone(),
            issued_at: value.issued_at.clone(),
            due_at: value.due_at.clone(),
            source_quote_id: value.source_quote_id.clone(),
            items: value
                .items
                .iter()
                .map(OutgoingInvoiceItemForm::from)
                .collect(),
        }
    }
}

impl From<&OutgoingInvoiceItem> for OutgoingInvoiceItemForm {
    fn from(value: &OutgoingInvoiceItem) -> Self {
        Self {
            item_id: value.item_id.clone(),
            description: value.description.clone(),
            quantity: value.quantity.clone(),
            unit_price: value.unit_price.clone(),
            original_unit_price: value.original_unit_price.clone(),
            discount_percent: value.discount_percent.clone(),
            tax_rate: normalize_tax_rate(&value.tax_rate),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct IncomingInvoice {
    id: String,
    invoice_number: String,
    supplier_id: String,
    status: String,
    cash_discount_term_id: Option<String>,
    invoice_date: Option<String>,
    due_at: Option<String>,
    items: Vec<IncomingInvoiceItem>,
}

#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
struct IncomingInvoiceItem {
    id: String,
    line_number: i32,
    item_id: Option<String>,
    description: String,
    quantity: String,
    unit_price: String,
    tax_rate: String,
}

#[derive(Debug, Clone, Serialize)]
struct IncomingInvoiceForm {
    invoice_number: String,
    supplier_id: String,
    status: String,
    cash_discount_term_id: Option<String>,
    invoice_date: Option<String>,
    due_at: Option<String>,
    items: Vec<IncomingInvoiceItemForm>,
}

#[derive(Debug, Clone, Serialize)]
struct IncomingInvoiceItemForm {
    item_id: Option<String>,
    description: String,
    quantity: String,
    unit_price: String,
    tax_rate: String,
}

impl Default for IncomingInvoiceForm {
    fn default() -> Self {
        Self {
            invoice_number: String::new(),
            supplier_id: String::new(),
            status: "received".to_string(),
            cash_discount_term_id: None,
            invoice_date: None,
            due_at: None,
            items: vec![IncomingInvoiceItemForm::default()],
        }
    }
}

impl Default for IncomingInvoiceItemForm {
    fn default() -> Self {
        Self {
            item_id: None,
            description: String::new(),
            quantity: "1".to_string(),
            unit_price: "0".to_string(),
            tax_rate: "19".to_string(),
        }
    }
}

impl From<&IncomingInvoice> for IncomingInvoiceForm {
    fn from(value: &IncomingInvoice) -> Self {
        Self {
            invoice_number: value.invoice_number.clone(),
            supplier_id: value.supplier_id.clone(),
            status: value.status.clone(),
            cash_discount_term_id: value.cash_discount_term_id.clone(),
            invoice_date: value.invoice_date.clone(),
            due_at: value.due_at.clone(),
            items: value
                .items
                .iter()
                .map(IncomingInvoiceItemForm::from)
                .collect(),
        }
    }
}

impl From<&IncomingInvoiceItem> for IncomingInvoiceItemForm {
    fn from(value: &IncomingInvoiceItem) -> Self {
        Self {
            item_id: value.item_id.clone(),
            description: value.description.clone(),
            quantity: value.quantity.clone(),
            unit_price: value.unit_price.clone(),
            tax_rate: normalize_tax_rate(&value.tax_rate),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct Activity {
    id: String,
    activity_number: Option<String>,
    activity_type: String,
    title: String,
    body: String,
    status: String,
    priority: String,
    due_at: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
struct ActivityForm {
    activity_number: Option<String>,
    activity_type: String,
    title: String,
    body: String,
    status: String,
    priority: String,
    due_at: Option<String>,
}
impl Default for ActivityForm {
    fn default() -> Self {
        Self {
            activity_number: None,
            activity_type: "task".to_string(),
            title: String::new(),
            body: String::new(),
            status: "active".to_string(),
            priority: "normal".to_string(),
            due_at: None,
        }
    }
}
impl From<&Activity> for ActivityForm {
    fn from(value: &Activity) -> Self {
        Self {
            activity_number: value.activity_number.clone(),
            activity_type: value.activity_type.clone(),
            title: value.title.clone(),
            body: value.body.clone(),
            status: value.status.clone(),
            priority: value.priority.clone(),
            due_at: value.due_at.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
struct PriceListImportForm {
    source_name: String,
    content: String,
    delimiter: Option<String>,
}

impl Default for PriceListImportForm {
    fn default() -> Self {
        Self {
            source_name: "Preisliste.csv".to_string(),
            content: "item_number;name;manufacturer_code;unit;tax_rate;purchase_price;sales_price;supplier_number;supplier_item_number;currency\nAR-IMPORT-1;Importartikel;HERST-001;Stck;19,00;10,00;25,00;LI000000001;EXT-4711;EUR".to_string(),
            delimiter: Some(";".to_string()),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct PriceListImportPreview {
    rows: Vec<PriceListImportRow>,
    total_rows: usize,
    valid_rows: usize,
    error_rows: usize,
}

#[derive(Debug, Clone, Deserialize)]
struct PriceListImportRow {
    row_number: usize,
    item_number: String,
    name: String,
    manufacturer_code: Option<String>,
    unit: String,
    tax_rate: String,
    purchase_price: Option<String>,
    sales_price: Option<String>,
    supplier_number: Option<String>,
    supplier_item_number: Option<String>,
    currency: Option<String>,
    action: String,
    error: Option<String>,
}

#[derive(Debug, Clone, Deserialize)]
struct PriceListImportApplyResponse {
    import_id: String,
    applied_rows: usize,
    error_rows: usize,
}

#[derive(Debug, Clone, Deserialize)]
struct ApiConnector {
    id: String,
    code: String,
    name: String,
    connector_type: String,
    config: serde_json::Value,
    is_active: bool,
    sync_interval_minutes: Option<i32>,
    last_sync_at: Option<String>,
}

#[derive(Debug, Clone)]
struct ApiConnectorForm {
    code: String,
    name: String,
    connector_type: String,
    config: String,
    is_active: bool,
    sync_interval_minutes: Option<i32>,
}

#[derive(Debug, Clone, Serialize)]
struct ApiConnectorPayload {
    code: String,
    name: String,
    connector_type: String,
    config: serde_json::Value,
    is_active: bool,
    sync_interval_minutes: Option<i32>,
}

impl Default for ApiConnectorForm {
    fn default() -> Self {
        Self {
            code: String::new(),
            name: String::new(),
            connector_type: "generic_price_api".to_string(),
            config: "{\n  \"base_url\": \"https://example.invalid\",\n  \"api_key\": \"dev\"\n}"
                .to_string(),
            is_active: true,
            sync_interval_minutes: Some(1440),
        }
    }
}

impl ApiConnectorForm {
    fn payload(&self) -> Result<ApiConnectorPayload, serde_json::Error> {
        Ok(ApiConnectorPayload {
            code: self.code.clone(),
            name: self.name.clone(),
            connector_type: self.connector_type.clone(),
            config: serde_json::from_str(self.config.trim().if_empty("{}"))?,
            is_active: self.is_active,
            sync_interval_minutes: self.sync_interval_minutes,
        })
    }
}

trait EmptyStrFallback {
    fn if_empty<'a>(&'a self, fallback: &'a str) -> &'a str;
}

impl EmptyStrFallback for str {
    fn if_empty<'a>(&'a self, fallback: &'a str) -> &'a str {
        if self.is_empty() {
            fallback
        } else {
            self
        }
    }
}

impl From<&ApiConnector> for ApiConnectorForm {
    fn from(value: &ApiConnector) -> Self {
        Self {
            code: value.code.clone(),
            name: value.name.clone(),
            connector_type: value.connector_type.clone(),
            config: serde_json::to_string_pretty(&value.config)
                .unwrap_or_else(|_| "{}".to_string()),
            is_active: value.is_active,
            sync_interval_minutes: value.sync_interval_minutes,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
struct ApiConnectorSyncResponse {
    synced: bool,
    id: String,
    applied_rows: usize,
    error_rows: usize,
}

#[derive(Debug, Clone, Deserialize)]
struct PriceRule {
    id: String,
    code: String,
    name: String,
    source_type: String,
    source_id: Option<String>,
    markup_percent: String,
    rounding_mode: String,
    is_active: bool,
}

#[derive(Debug, Clone, Serialize)]
struct PriceRuleForm {
    code: String,
    name: String,
    source_type: String,
    source_id: Option<String>,
    markup_percent: String,
    rounding_mode: String,
    is_active: bool,
}

impl Default for PriceRuleForm {
    fn default() -> Self {
        Self {
            code: String::new(),
            name: String::new(),
            source_type: "import".to_string(),
            source_id: None,
            markup_percent: "0.00".to_string(),
            rounding_mode: "none".to_string(),
            is_active: true,
        }
    }
}

impl From<&PriceRule> for PriceRuleForm {
    fn from(value: &PriceRule) -> Self {
        Self {
            code: value.code.clone(),
            name: value.name.clone(),
            source_type: value.source_type.clone(),
            source_id: value.source_id.clone(),
            markup_percent: value.markup_percent.clone(),
            rounding_mode: value.rounding_mode.clone(),
            is_active: value.is_active,
        }
    }
}

async fn login_user(api_base_url: &str, email: String, password: String) -> LoginResult {
    let url = format!("{}/api/v1/auth/login", api_base_url.trim_end_matches('/'));
    let response = reqwest::Client::new()
        .post(url)
        .json(&LoginRequest { email, password })
        .send()
        .await
        .map_err(|error| format!("Login fehlgeschlagen: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        let message = serde_json::from_str::<serde_json::Value>(&text)
            .ok()
            .and_then(|value| {
                value
                    .get("message")
                    .and_then(|item| item.as_str())
                    .map(str::to_string)
            })
            .unwrap_or_else(|| format!("HTTP {status}"));
        return Err(message);
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn get_user_navigation_settings(
    api_base_url: &str,
    access_token: &str,
) -> Result<UserNavigationSettings, String> {
    let url = format!(
        "{}/api/v1/users/me/settings/navigation",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .get(url)
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Menüeinstellung konnte nicht geladen werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn put_user_navigation_settings(
    api_base_url: &str,
    access_token: &str,
    settings: UserNavigationSettings,
) -> Result<UserNavigationSettings, String> {
    let url = format!(
        "{}/api/v1/users/me/settings/navigation",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .put(url)
        .bearer_auth(access_token)
        .json(&settings)
        .send()
        .await
        .map_err(|error| format!("Menüeinstellung konnte nicht gespeichert werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn dev_bootstrap_local(
    api_base_url: &str,
    organization_name: String,
    email: String,
) -> DevBootstrapResult {
    let url = format!(
        "{}/api/v1/dev/bootstrap-local",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .post(url)
        .json(&DevBootstrapRequest {
            organization_name,
            email,
        })
        .send()
        .await
        .map_err(|error| format!("Dev-Bootstrap fehlgeschlagen: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        let message = serde_json::from_str::<serde_json::Value>(&text)
            .ok()
            .and_then(|value| {
                value
                    .get("message")
                    .and_then(|item| item.as_str())
                    .map(str::to_string)
            })
            .unwrap_or_else(|| format!("HTTP {status}"));
        return Err(message);
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn register_organization(
    api_base_url: &str,
    organization_name: String,
    email: String,
    accept_terms: bool,
) -> RegistrationResult {
    let url = format!(
        "{}/api/v1/registration/organization",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .post(url)
        .json(&RegistrationRequest {
            organization_name,
            email,
            accept_terms,
        })
        .send()
        .await
        .map_err(|error| format!("Registrierung fehlgeschlagen: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        let message = serde_json::from_str::<serde_json::Value>(&text)
            .ok()
            .and_then(|value| {
                value
                    .get("message")
                    .and_then(|item| item.as_str())
                    .map(str::to_string)
            })
            .unwrap_or_else(|| format!("HTTP {status}"));
        return Err(message);
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn list_users(api_base_url: &str, access_token: &str) -> UsersResult {
    let url = format!(
        "{}/api/v1/organizations/current/users",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .get(url)
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Benutzer konnten nicht geladen werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn invite_organization_user(
    api_base_url: &str,
    access_token: &str,
    email: String,
    roles: Vec<String>,
) -> EmptyResult {
    let url = format!(
        "{}/api/v1/organizations/current/invitations",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .post(url)
        .bearer_auth(access_token)
        .json(&InviteUserRequest { email, roles })
        .send()
        .await
        .map_err(|error| format!("Einladung fehlgeschlagen: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

async fn update_user_roles(
    api_base_url: &str,
    access_token: &str,
    user_id: &str,
    roles: Vec<String>,
) -> EmptyResult {
    let url = format!(
        "{}/api/v1/organizations/current/users/{}/roles",
        api_base_url.trim_end_matches('/'),
        user_id
    );
    let response = reqwest::Client::new()
        .patch(url)
        .bearer_auth(access_token)
        .json(&UpdateUserRolesRequest { roles })
        .send()
        .await
        .map_err(|error| format!("Benutzerrechte konnten nicht gespeichert werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

async fn save_organization_setup(
    api_base_url: &str,
    access_token: &str,
    payload: OrganizationSetupForm,
) -> EmptyResult {
    let url = format!(
        "{}/api/v1/organizations/current/setup",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .put(url)
        .bearer_auth(access_token)
        .json(&payload)
        .send()
        .await
        .map_err(|error| format!("Firmendaten konnten nicht gespeichert werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

async fn load_organization_setup(
    api_base_url: &str,
    access_token: &str,
) -> Result<Option<OrganizationSetupForm>, String> {
    let url = format!(
        "{}/api/v1/organizations/current/setup",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .get(url)
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Firmendaten konnten nicht geladen werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    let data = serde_json::from_str::<OrganizationSetupResponse>(&text)
        .map_err(|error| format!("Antwort ist ungültig: {error}"))?;
    Ok(data.setup)
}

async fn list_registrations(api_base_url: &str) -> RegistrationsResult {
    let url = format!(
        "{}/api/v1/admin/organization-registrations",
        api_base_url.trim_end_matches('/')
    );
    let response = reqwest::Client::new()
        .get(url)
        .send()
        .await
        .map_err(|error| format!("Registrierungen konnten nicht geladen werden: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn approve_registration(
    api_base_url: &str,
    registration_id: &str,
) -> Result<ApproveRegistrationResponse, String> {
    let url = format!(
        "{}/api/v1/admin/organization-registrations/{}/approve",
        api_base_url.trim_end_matches('/'),
        registration_id
    );
    let response = reqwest::Client::new()
        .post(url)
        .json(&serde_json::json!({}))
        .send()
        .await
        .map_err(|error| format!("Freischaltung fehlgeschlagen: {error}"))?;

    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;

    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }

    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn list_customers(api_base_url: &str, access_token: &str) -> CustomersResult {
    let url = format!("{}/api/v1/customers", api_base_url.trim_end_matches('/'));
    let response = reqwest::Client::new()
        .get(url)
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Kunden konnten nicht geladen werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn save_customer(
    api_base_url: &str,
    access_token: &str,
    customer_id: Option<String>,
    payload: CustomerForm,
) -> Result<Customer, String> {
    let client = reqwest::Client::new();
    let response = if let Some(customer_id) = customer_id {
        client
            .put(format!(
                "{}/api/v1/customers/{}",
                api_base_url.trim_end_matches('/'),
                customer_id
            ))
            .bearer_auth(access_token)
            .json(&payload)
            .send()
            .await
    } else {
        client
            .post(format!(
                "{}/api/v1/customers",
                api_base_url.trim_end_matches('/')
            ))
            .bearer_auth(access_token)
            .json(&payload)
            .send()
            .await
    }
    .map_err(|error| format!("Kunde konnte nicht gespeichert werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn delete_customer(api_base_url: &str, access_token: &str, customer_id: &str) -> EmptyResult {
    let response = reqwest::Client::new()
        .delete(format!(
            "{}/api/v1/customers/{}",
            api_base_url.trim_end_matches('/'),
            customer_id
        ))
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Kunde konnte nicht deaktiviert werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

async fn list_master_records<T: for<'de> Deserialize<'de>>(
    api_base_url: &str,
    access_token: &str,
    path: &str,
) -> Result<Vec<T>, String> {
    let response = reqwest::Client::new()
        .get(format!("{}{}", api_base_url.trim_end_matches('/'), path))
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Daten konnten nicht geladen werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn save_master_record<P: Serialize, R: for<'de> Deserialize<'de>>(
    api_base_url: &str,
    access_token: &str,
    path: &str,
    id: Option<String>,
    payload: P,
) -> Result<R, String> {
    let client = reqwest::Client::new();
    let request = if let Some(id) = id {
        client.put(format!(
            "{}{}/{}",
            api_base_url.trim_end_matches('/'),
            path,
            id
        ))
    } else {
        client.post(format!("{}{}", api_base_url.trim_end_matches('/'), path))
    };
    let response = request
        .bearer_auth(access_token)
        .json(&payload)
        .send()
        .await
        .map_err(|error| format!("Datensatz konnte nicht gespeichert werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn save_master_record_without_id<P: Serialize, R: for<'de> Deserialize<'de>>(
    api_base_url: &str,
    access_token: &str,
    path: &str,
    payload: P,
) -> Result<R, String> {
    let response = reqwest::Client::new()
        .put(format!("{}{}", api_base_url.trim_end_matches('/'), path))
        .bearer_auth(access_token)
        .json(&payload)
        .send()
        .await
        .map_err(|error| format!("Datensatz konnte nicht gespeichert werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn post_master_record<P: Serialize, R: for<'de> Deserialize<'de>>(
    api_base_url: &str,
    access_token: &str,
    path: &str,
    payload: P,
) -> Result<R, String> {
    let response = reqwest::Client::new()
        .post(format!("{}{}", api_base_url.trim_end_matches('/'), path))
        .bearer_auth(access_token)
        .json(&payload)
        .send()
        .await
        .map_err(|error| format!("Aktion konnte nicht ausgeführt werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if !status.is_success() {
        return Err(error_message_from_response(status, &text));
    }
    serde_json::from_str(&text).map_err(|error| format!("Antwort ist ungültig: {error}"))
}

async fn reserve_next_number(
    api_base_url: &str,
    access_token: &str,
    code: &str,
) -> Result<String, String> {
    let path = format!("/api/v1/number-ranges/{code}/next");
    let response: NextNumberResponse =
        post_master_record(api_base_url, access_token, &path, serde_json::json!({})).await?;
    Ok(response.number)
}

async fn post_empty_action(api_base_url: &str, access_token: &str, path: &str) -> EmptyResult {
    let response = reqwest::Client::new()
        .post(format!("{}{}", api_base_url.trim_end_matches('/'), path))
        .bearer_auth(access_token)
        .json(&serde_json::json!({}))
        .send()
        .await
        .map_err(|error| format!("Aktion konnte nicht ausgeführt werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

async fn delete_master_record(
    api_base_url: &str,
    access_token: &str,
    path: &str,
    id: &str,
) -> EmptyResult {
    let response = reqwest::Client::new()
        .delete(format!(
            "{}{}/{}",
            api_base_url.trim_end_matches('/'),
            path,
            id
        ))
        .bearer_auth(access_token)
        .send()
        .await
        .map_err(|error| format!("Datensatz konnte nicht deaktiviert werden: {error}"))?;
    let status = response.status();
    let text = response
        .text()
        .await
        .map_err(|error| format!("Antwort konnte nicht gelesen werden: {error}"))?;
    if status.is_success() {
        Ok(())
    } else {
        Err(error_message_from_response(status, &text))
    }
}

fn error_message_from_response(status: reqwest::StatusCode, text: &str) -> String {
    serde_json::from_str::<serde_json::Value>(text)
        .ok()
        .and_then(|value| {
            value
                .get("message")
                .and_then(|item| item.as_str())
                .map(str::to_string)
        })
        .unwrap_or_else(|| format!("HTTP {status}"))
}
