use crate::components::keyboard;
use iced::widget::text::Shaping;
use iced::widget::{Space, column, container, row, scrollable, text};
use iced::{Color, Element, Length};

use bds_core::engine::metadata_diff::{DiffReport, RepairDirection};
use bds_core::i18n::UiLocale;

use crate::app::Message;
use crate::components::inputs;
use crate::i18n::{t, tw};

#[derive(Debug, Clone, Default)]
pub struct MetadataDiffState {
    pub is_running: bool,
    pub is_repairing: bool,
    pub report: Option<DiffReport>,
    pub error_message: Option<String>,
}

pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, Message> {
    let run = keyboard::button(text(t(locale, "metadataDiff.run")).size(13))
        .on_press_maybe(
            (!state.is_running && !state.is_repairing).then_some(Message::RunMetadataDiff),
        )
        .hotkey('r')
        .style(inputs::primary_button)
        .padding([6, 16]);
    let mut content = column![
        row![
            text(t(locale, "tabBar.metadataDiff"))
                .size(24)
                .shaping(Shaping::Advanced)
                .color(Color::from_rgb(0.88, 0.88, 0.92)),
            Space::new().width(Length::Fill),
            run,
        ]
        .align_y(iced::Alignment::Center),
    ]
    .spacing(16);

    if state.is_running {
        content = content.push(message_card(t(locale, "metadataDiff.running")));
    } else if let Some(error) = &state.error_message {
        content = content.push(message_card(error.clone()));
    } else if let Some(report) = &state.report {
        if report.diffs.is_empty() && report.orphans.is_empty() && report.errors.is_empty() {
            content = content.push(message_card(t(locale, "metadataDiff.clean")));
        }
        for (index, item) in report.diffs.iter().enumerate() {
            let fields = item
                .fields
                .iter()
                .fold(column!().spacing(4), |column, field| {
                    column.push(
                        text(tw(
                            locale,
                            "metadataDiff.field",
                            &[
                                ("field", &field.field_name),
                                ("database", &field.db_value),
                                ("file", &field.file_value),
                            ],
                        ))
                        .size(12)
                        .color(Color::from_rgb(0.72, 0.72, 0.78)),
                    )
                });
            let actions = row![
                keyboard::button(text(t(locale, "metadataDiff.fileToDb")).size(12))
                    .on_press_maybe((!state.is_repairing).then_some(
                        Message::RepairMetadataDiffItem {
                            index,
                            direction: RepairDirection::FileToDatabase,
                        },
                    ))
                    .style(inputs::secondary_button)
                    .padding([5, 10]),
                keyboard::button(text(t(locale, "metadataDiff.dbToFile")).size(12))
                    .on_press_maybe((!state.is_repairing).then_some(
                        Message::RepairMetadataDiffItem {
                            index,
                            direction: RepairDirection::DatabaseToFile,
                        },
                    ))
                    .style(inputs::secondary_button)
                    .padding([5, 10]),
            ]
            .spacing(8);
            content = content.push(inputs::card(
                column![
                    text(tw(
                        locale,
                        "metadataDiff.entity",
                        &[("entity", &item.entity_type), ("path", &item.file_path)],
                    ))
                    .size(15)
                    .color(Color::from_rgb(0.86, 0.86, 0.92)),
                    fields,
                    actions,
                ]
                .spacing(10),
            ));
        }
        if !report.orphans.is_empty() {
            let rows = report.orphans.iter().enumerate().fold(
                column!().spacing(8),
                |column, (index, orphan)| {
                    let mut item = row![
                        text(tw(
                            locale,
                            "metadataDiff.orphan",
                            &[("reason", &orphan.reason), ("path", &orphan.file_path)],
                        ))
                        .size(12),
                        Space::new().width(Length::Fill),
                    ]
                    .align_y(iced::Alignment::Center);
                    if orphan.reason == "file_without_db_entry" {
                        item = item.push(
                            keyboard::button(text(t(locale, "metadataDiff.importOrphan")).size(12))
                                .on_press_maybe(
                                    (!state.is_repairing)
                                        .then_some(Message::ImportMetadataOrphan(index)),
                                )
                                .style(inputs::secondary_button)
                                .padding([5, 10]),
                        );
                    }
                    column.push(item)
                },
            );
            content = content.push(inputs::card(
                column![text(t(locale, "metadataDiff.orphans")).size(16), rows].spacing(8),
            ));
        }
        if !report.errors.is_empty() {
            content = content.push(inputs::card(
                column![
                    text(t(locale, "metadataDiff.errors")).size(16),
                    report
                        .errors
                        .iter()
                        .fold(column!().spacing(4), |column, error| {
                            column.push(text(error.clone()).size(12))
                        }),
                ]
                .spacing(8),
            ));
        }
    } else {
        content = content.push(message_card(t(locale, "metadataDiff.idle")));
    }

    container(
        scrollable(content)
            .direction(scrollable::Direction::Vertical(inputs::compact_scrollbar()))
            .style(inputs::scrollable_style),
    )
    .width(Length::Fill)
    .height(Length::Fill)
    .padding(24)
    .into()
}

fn message_card(value: String) -> Element<'static, Message> {
    inputs::card(
        text(value)
            .size(14)
            .color(Color::from_rgb(0.66, 0.66, 0.72)),
    )
    .into()
}

#[cfg(test)]
mod tests {
    use super::*;
    use bds_core::engine::metadata_diff::{DiffField, EntityDiff};

    #[test]
    fn renders_all_portable_project_metadata_diff_cards() {
        let state = MetadataDiffState {
            report: Some(DiffReport {
                diffs: ["categories", "category_meta", "publishing"]
                    .into_iter()
                    .map(|entity_type| EntityDiff {
                        entity_type: entity_type.into(),
                        entity_id: "p1".into(),
                        file_path: format!("meta/{entity_type}.json"),
                        fields: vec![DiffField {
                            field_name: "value".into(),
                            db_value: "database".into(),
                            file_value: "filesystem".into(),
                        }],
                    })
                    .collect(),
                ..Default::default()
            }),
            ..Default::default()
        };

        let _: Element<'_, Message> = view(&state, UiLocale::En);
    }
}
