use ratatui::style::Color;

static COLOR_RX: Color = Color::Rgb(255, 233, 193);
static COLOR_TX: Color = Color::Rgb(205, 140, 140);

/// The macro accepts a list of struct names with key names
/// Returns a struct where every key name is an Option<String>, with the correct derived attributes
macro_rules! optional_config_struct {
    ($($struct_name:ident, $($key_name:ident),*);*) => {
        $(
            #[derive(Debug, serde::Deserialize, Clone, PartialEq, Eq)]
            struct $struct_name {
                $(
                    $key_name: Option<String>,
                )*
            }
        )*
    };
}

/// The macro accepts a list of struct names with key names
macro_rules! config_struct {
    ($($struct_name:ident, $($key_name:ident),*);*) => {
        $(
            #[derive(Debug, Clone, PartialEq, Eq, Copy)]
            pub struct $struct_name {
                $(
                    pub $key_name: Color,
                )*
            }
        )*
    };
}

impl AppColors {
    fn map_color(color_str: Option<&str>, setter: &mut Color) {
        color_str.map(|i| i.parse::<Color>().map(|i| *setter = i));
    }
}

impl From<Option<ConfigColors>> for AppColors {
    #[allow(clippy::too_many_lines)]
    fn from(value: Option<ConfigColors>) -> Self {
        let mut app_colors = Self::new();

        if let Some(config_colors) = value {
            // Heading bar
            if let Some(hb) = config_colors.headers_bar {
                Self::map_color(
                    hb.background.as_deref(),
                    &mut app_colors.headers_bar.background,
                );
                Self::map_color(
                    hb.loading_spinner.as_deref(),
                    &mut app_colors.headers_bar.loading_spinner,
                );
                Self::map_color(hb.text.as_deref(), &mut app_colors.headers_bar.text);
                Self::map_color(
                    hb.text_selected.as_deref(),
                    &mut app_colors.headers_bar.text_selected,
                );
            }

            // Selectable panel borders
            if let Some(b) = config_colors.borders {
                Self::map_color(b.selected.as_deref(), &mut app_colors.borders.selected);
                Self::map_color(b.unselected.as_deref(), &mut app_colors.borders.unselected);
            }

            // Error Popup
            if let Some(ep) = config_colors.popup_error {
                Self::map_color(
                    ep.background.as_deref(),
                    &mut app_colors.popup_error.background,
                );
                Self::map_color(ep.text.as_deref(), &mut app_colors.popup_error.text);
            }

            // Filter panel
            if let Some(fc) = config_colors.filter {
                Self::map_color(fc.background.as_deref(), &mut app_colors.filter.background);
                Self::map_color(fc.highlight.as_deref(), &mut app_colors.filter.highlight);

                Self::map_color(
                    fc.selected_filter_background.as_deref(),
                    &mut app_colors.filter.selected_filter_background,
                );
                Self::map_color(
                    fc.selected_filter_text.as_deref(),
                    &mut app_colors.filter.selected_filter_text,
                );
                Self::map_color(fc.text.as_deref(), &mut app_colors.filter.text);
            }

            // Log search
            if let Some(ls) = config_colors.log_search {
                Self::map_color(
                    ls.background.as_deref(),
                    &mut app_colors.log_search.background,
                );
                Self::map_color(
                    ls.highlight.as_deref(),
                    &mut app_colors.log_search.highlight,
                );

                Self::map_color(
                    ls.button_text.as_deref(),
                    &mut app_colors.log_search.button_text,
                );
                Self::map_color(ls.text.as_deref(), &mut app_colors.log_search.text);
            }

            // Help Popup
            if let Some(hp) = config_colors.popup_help {
                Self::map_color(
                    hp.background.as_deref(),
                    &mut app_colors.popup_help.background,
                );
                Self::map_color(hp.text.as_deref(), &mut app_colors.popup_help.text);
                Self::map_color(
                    hp.text_highlight.as_deref(),
                    &mut app_colors.popup_help.text_highlight,
                );
            }

            // Info Popup
            if let Some(ip) = config_colors.popup_info {
                Self::map_color(
                    ip.background.as_deref(),
                    &mut app_colors.popup_info.background,
                );
                Self::map_color(ip.text.as_deref(), &mut app_colors.popup_info.text);
            }

            // Delete Popup
            if let Some(dp) = config_colors.popup_delete {
                Self::map_color(
                    dp.background.as_deref(),
                    &mut app_colors.popup_delete.background,
                );
                Self::map_color(dp.text.as_deref(), &mut app_colors.popup_delete.text);
                Self::map_color(
                    dp.text_highlight.as_deref(),
                    &mut app_colors.popup_delete.text_highlight,
                );
            }

            // Chart Cpu
            if let Some(cc) = config_colors.chart_cpu {
                Self::map_color(
                    cc.background.as_deref(),
                    &mut app_colors.chart_cpu.background,
                );
                Self::map_color(cc.border.as_deref(), &mut app_colors.chart_cpu.border);
                Self::map_color(cc.max.as_deref(), &mut app_colors.chart_cpu.max);
                Self::map_color(cc.points.as_deref(), &mut app_colors.chart_cpu.points);
                Self::map_color(cc.title.as_deref(), &mut app_colors.chart_cpu.title);
                Self::map_color(cc.y_axis.as_deref(), &mut app_colors.chart_cpu.y_axis);
            }

            // Chart Memory
            if let Some(cm) = config_colors.chart_memory {
                Self::map_color(
                    cm.background.as_deref(),
                    &mut app_colors.chart_memory.background,
                );
                Self::map_color(cm.border.as_deref(), &mut app_colors.chart_memory.border);
                Self::map_color(cm.max.as_deref(), &mut app_colors.chart_memory.max);
                Self::map_color(cm.points.as_deref(), &mut app_colors.chart_memory.points);
                Self::map_color(cm.title.as_deref(), &mut app_colors.chart_memory.title);
                Self::map_color(cm.y_axis.as_deref(), &mut app_colors.chart_memory.y_axis);
            }

            // Chart ports
            if let Some(cp) = config_colors.chart_ports {
                Self::map_color(
                    cp.background.as_deref(),
                    &mut app_colors.chart_ports.background,
                );
                Self::map_color(cp.border.as_deref(), &mut app_colors.chart_ports.border);
                Self::map_color(cp.headings.as_deref(), &mut app_colors.chart_ports.headings);
                Self::map_color(cp.text.as_deref(), &mut app_colors.chart_ports.text);
                Self::map_color(cp.title.as_deref(), &mut app_colors.chart_ports.title);
            }

            // Containers
            if let Some(c) = config_colors.containers {
                Self::map_color(
                    c.background.as_deref(),
                    &mut app_colors.containers.background,
                );
                Self::map_color(c.icon.as_deref(), &mut app_colors.containers.icon);
                Self::map_color(c.text.as_deref(), &mut app_colors.containers.text);
                Self::map_color(c.text_rx.as_deref(), &mut app_colors.containers.text_rx);
                Self::map_color(c.text_tx.as_deref(), &mut app_colors.containers.text_tx);
            }

            // Commands
            if let Some(cc) = config_colors.commands {
                Self::map_color(
                    cc.background.as_deref(),
                    &mut app_colors.commands.background,
                );
                Self::map_color(cc.pause.as_deref(), &mut app_colors.commands.pause);
                Self::map_color(cc.restart.as_deref(), &mut app_colors.commands.restart);
                Self::map_color(cc.stop.as_deref(), &mut app_colors.commands.stop);
                Self::map_color(cc.delete.as_deref(), &mut app_colors.commands.start);
                Self::map_color(cc.resume.as_deref(), &mut app_colors.commands.resume);
                Self::map_color(cc.start.as_deref(), &mut app_colors.commands.start);
            }

            // Logs panel
            if let Some(cl) = config_colors.logs {
                Self::map_color(cl.background.as_deref(), &mut app_colors.logs.background);
                Self::map_color(cl.text.as_deref(), &mut app_colors.logs.text);
            }

            // Container State
            if let Some(cs) = config_colors.container_state {
                Self::map_color(cs.dead.as_deref(), &mut app_colors.container_state.dead);
                Self::map_color(cs.exited.as_deref(), &mut app_colors.container_state.exited);
                Self::map_color(cs.paused.as_deref(), &mut app_colors.container_state.paused);
                Self::map_color(
                    cs.removing.as_deref(),
                    &mut app_colors.container_state.removing,
                );
                Self::map_color(
                    cs.restarting.as_deref(),
                    &mut app_colors.container_state.restarting,
                );
                Self::map_color(
                    cs.running_healthy.as_deref(),
                    &mut app_colors.container_state.running_healthy,
                );
                Self::map_color(
                    cs.running_unhealthy.as_deref(),
                    &mut app_colors.container_state.running_unhealthy,
                );
                Self::map_color(
                    cs.unknown.as_deref(),
                    &mut app_colors.container_state.unknown,
                );
            }
        }
        app_colors
    }
}

const ORANGE: Color = Color::Rgb(255, 178, 36);

optional_config_struct!(
    ConfigBackgroundText, background, text;
    ConfigBackgroundTextHighlight, background, text, text_highlight;
    ConfigBorders, selected, unselected;
    ConfigChartBandwidth, background, border, max_rx, max_tx, title_tx, title_rx, points_rx, points_tx, y_axis;

    ConfigChartCpu, background, border, order, title, max, points,y_axis;
    ConfigChartMemory, background, border, title, max, points, y_axis;
    ConfigChartPorts, background, border, title, headings, text;
    ConfigCommands, background, pause, restart, stop, delete, resume, start;
    ConfigContainers, background, icon, text, text_rx, text_tx;
    ConfigContainerState, background, dead, exited, paused, removing, restarting, running_healthy, running_unhealthy, unknown;
    ConfigFilter, background, text, selected_filter_background, selected_filter_text, highlight;
     ConfigLogSearch, background, text, button_text, highlight;
    ConfigHeadersBar, background, loading_spinner, text, text_selected;
    ConfigLogs, background, text
);

config_struct!(
    Borders, selected, unselected;
    ChartCpu, background, border, title, max, points, y_axis;
    ChartMemory, background, border, title, max, points, y_axis;
    ChartBandwidth, background, border, max_rx, max_tx, title_rx, title_tx, points_rx, points_tx, y_axis;

    ChartPorts, background, border, title, headings, text;
    Commands, background, pause, restart, stop, delete, resume, start;
    Containers, background, icon, text, text_rx, text_tx;
    ContainerState, dead, exited, paused, removing, restarting, running_healthy, running_unhealthy, unknown;
    Filter, background, text, selected_filter_background, selected_filter_text, highlight;
    LogSearch, background, text, button_text, highlight;
    HeadersBar, background, text_selected, loading_spinner, text;
    Logs, background, text;
    PopupDelete, background, text, text_highlight;
    PopupError, background, text;
    PopupHelp, background, text, text_highlight;
    PopupInfo, background, text
);

#[derive(Debug, serde::Deserialize, Clone, PartialEq, Eq)]
pub struct ConfigColors {
    borders: Option<ConfigBorders>,
    chart_cpu: Option<ConfigChartCpu>,
    chart_memory: Option<ConfigChartMemory>,
    chart_bandwidth: Option<ConfigChartBandwidth>,
    chart_ports: Option<ConfigChartPorts>,
    commands: Option<ConfigCommands>,
    container_state: Option<ConfigContainerState>,
    containers: Option<ConfigContainers>,
    filter: Option<ConfigFilter>,
    log_search: Option<ConfigLogSearch>,
    headers_bar: Option<ConfigHeadersBar>,
    logs: Option<ConfigLogs>,
    popup_delete: Option<ConfigBackgroundTextHighlight>,
    popup_error: Option<ConfigBackgroundText>,
    popup_help: Option<ConfigBackgroundTextHighlight>,
    popup_info: Option<ConfigBackgroundText>,
}

/// Default colours for the header bar
impl HeadersBar {
    const fn new() -> Self {
        Self {
            background: Color::Magenta,
            loading_spinner: Color::White,
            text: Color::Black,
            text_selected: Color::Gray,
        }
    }
}

/// Default colours for the borders
impl Borders {
    const fn new() -> Self {
        Self {
            selected: Color::LightCyan,
            unselected: Color::Gray,
        }
    }
}

/// Default colours for the delete popup
impl Commands {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            pause: Color::Yellow,
            restart: Color::Magenta,
            stop: Color::Red,
            delete: Color::Gray,
            resume: Color::Blue,
            start: Color::Green,
        }
    }
}

/// Default colours for the Bandwidth chart
impl ChartBandwidth {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            border: Color::White,
            max_rx: COLOR_RX,
            title_rx: COLOR_RX,
            title_tx: COLOR_TX,
            max_tx: COLOR_TX,
            points_rx: COLOR_RX,
            points_tx: COLOR_TX,
            y_axis: Color::White,
        }
    }
}

/// Default colours for the CPU chart
impl ChartCpu {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            border: Color::White,
            title: Color::Green,
            max: ORANGE,
            points: Color::Magenta,
            y_axis: Color::White,
        }
    }
}

/// Default colours for the help popup
impl ChartMemory {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            border: Color::White,
            title: Color::Green,
            max: ORANGE,
            points: Color::Cyan,
            y_axis: Color::White,
        }
    }
}

/// Default colours for the help popup
impl ChartPorts {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            border: Color::White,
            title: Color::Green,
            headings: Color::Yellow,
            text: Color::White,
        }
    }
}

/// Default colours for the help popup
impl Containers {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            icon: Color::White,
            text: Color::Blue,
            text_rx: COLOR_RX,
            text_tx: COLOR_TX,
        }
    }
}

/// Default colours for the help popup
impl ContainerState {
    const fn new() -> Self {
        Self {
            paused: Color::Yellow,
            removing: Color::LightRed,
            restarting: Color::LightGreen,
            running_healthy: Color::Green,
            running_unhealthy: ORANGE,
            dead: Color::Red,
            exited: Color::Red,
            unknown: Color::Red,
        }
    }
}

/// Default colours for the filter panel
impl Filter {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            highlight: Color::Magenta,
            selected_filter_background: Color::Gray,
            selected_filter_text: Color::Black,
            text: Color::Gray,
        }
    }
}

/// Default colours for the log search
impl LogSearch {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            highlight: Color::Magenta,
            button_text: Color::Black,
            text: Color::Gray,
        }
    }
}

/// Default colours for the logs panel, only applied if color_logs is false
impl Logs {
    const fn new() -> Self {
        Self {
            background: Color::Reset,
            text: Color::Reset,
        }
    }
}

/// Default colours for the Error popup
impl PopupError {
    const fn new() -> Self {
        Self {
            background: Color::Red,
            text: Color::White,
        }
    }
}

/// Default colours for the info popup
impl PopupInfo {
    const fn new() -> Self {
        Self {
            background: Color::Blue,
            text: Color::White,
        }
    }
}

/// Default colours for the help popup
impl PopupHelp {
    const fn new() -> Self {
        Self {
            background: Color::Magenta,
            text: Color::Black,
            text_highlight: Color::White,
        }
    }
}

/// Default colours for the delete popup
impl PopupDelete {
    const fn new() -> Self {
        Self {
            background: Color::White,
            text: Color::Black,
            text_highlight: Color::Red,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct AppColors {
    pub borders: Borders,
    pub chart_cpu: ChartCpu,
    pub chart_memory: ChartMemory,
    pub chart_bandwidth: ChartBandwidth,
    pub chart_ports: ChartPorts,
    pub commands: Commands,
    pub container_state: ContainerState,
    pub containers: Containers,
    pub log_search: LogSearch,
    pub filter: Filter,
    pub headers_bar: HeadersBar,
    pub logs: Logs,
    pub popup_delete: PopupDelete,
    pub popup_error: PopupError,
    pub popup_help: PopupHelp,
    pub popup_info: PopupInfo,
}

impl AppColors {
    pub const fn new() -> Self {
        Self {
            borders: Borders::new(),
            chart_cpu: ChartCpu::new(),
            chart_memory: ChartMemory::new(),
            chart_bandwidth: ChartBandwidth::new(),
            chart_ports: ChartPorts::new(),
            commands: Commands::new(),
            container_state: ContainerState::new(),
            containers: Containers::new(),
            log_search: LogSearch::new(),
            filter: Filter::new(),
            headers_bar: HeadersBar::new(),
            logs: Logs::new(),
            popup_delete: PopupDelete::new(),
            popup_error: PopupError::new(),
            popup_help: PopupHelp::new(),
            popup_info: PopupInfo::new(),
        }
    }
}
