import csv
import shutil
from importlib import resources as importlib_resources
from pathlib import Path

from nicegui import ui

from nemo_library_etl.adapter.migman.enums import MigManTransformStep
from nemo_library_etl.adapter.migman.migmanutils import MigManUtils

from ..folder_actions import (
    render_folder_button,
    resolve_join_dir,
    resolve_mapping_dir,
)
from ..render_context import UIRenderContext


def render_transform(ctx: UIRenderContext) -> None:
    runtime = ctx.runtime
    global_config = runtime.global_config
    local_config = runtime.local_config
    merged_config = runtime.merged_config
    form_state = runtime.form_state
    state = runtime.state
    has_changes = runtime.has_changes
    active = runtime.active
    nav_buttons = runtime.nav_buttons
    controller = ctx.controller
    content_container = ctx.content_container
    transform_tab_state = ctx.transform_tab_state
    convenience_hooks = ctx.convenience_hooks
    migmanprojects = ctx.migmanprojects
    get_tip = ctx.get_tip
    mark_changed = ctx.mark_changed
    mark_saved = ctx.mark_saved
    set_active = ctx.set_active
    save_config = ctx.save_config
    reset_config = ctx.reset_config
    renderers = ctx.renderers
    self = ctx.ui_app

    def _get_migman_duckdb_path() -> Path:
        etl_dir = form_state["data"].get("etl_directory", "./etl/migman")
        return (Path(etl_dir) / "migman_etl.duckdb").resolve()

    def _collect_distinct_mapping_values(field_name: str) -> list[str]:
        field_name = (field_name or "").strip()
        if not field_name:
            return []

        setup_config = form_state["data"].get("setup", {})
        projects = setup_config.get("projects", []) or []
        if not projects:
            raise ValueError("No projects configured in Setup")

        duckdb_path = _get_migman_duckdb_path()
        if not duckdb_path.exists():
            raise FileNotFoundError(f"DuckDB file not found: {duckdb_path}")

        import duckdb

        distinct_values = set()
        found_join_table = False
        found_field_in_join_table = False

        with duckdb.connect(str(duckdb_path), read_only=True) as con:
            for project in projects:
                table_name = f"{MigManTransformStep.JOINS.value}_{project}"

                table_exists = con.execute(
                    """
                    SELECT 1
                    FROM information_schema.tables
                    WHERE table_name = ?
                    LIMIT 1
                    """,
                    [table_name],
                ).fetchone()

                if not table_exists:
                    continue

                found_join_table = True

                column_rows = con.execute(
                    """
                    SELECT column_name
                    FROM information_schema.columns
                    WHERE table_name = ?
                    """,
                    [table_name],
                ).fetchall()

                available_columns = {row[0] for row in column_rows}
                if field_name not in available_columns:
                    continue

                found_field_in_join_table = True

                safe_table_name = table_name.replace('"', '""')
                safe_field_name = field_name.replace('"', '""')

                value_rows = con.execute(
                    f'''
                    SELECT DISTINCT "{safe_field_name}"
                    FROM "{safe_table_name}"
                    WHERE "{safe_field_name}" IS NOT NULL
                    '''
                ).fetchall()

                for row in value_rows:
                    value = row[0]
                    if value is None:
                        continue

                    value_text = str(value).strip()
                    if value_text:
                        distinct_values.add(value_text)

        if not found_join_table:
            raise ValueError(
                "No join tables found. Please run the join step first."
            )

        if not found_field_in_join_table:
            raise ValueError(
                f"Field '{field_name}' was not found in any join result table"
            )

        return sorted(distinct_values)

    # transform_tab_state is defined globally (used by Help context + tab binding)
    # --- refreshable panels for Transform tabs ---
    @ui.refreshable
    def transform_join_panel() -> None:
        transform_config = form_state["data"].setdefault("transform", {})
        render_transform_join_config(transform_config)

    @ui.refreshable
    def transform_mapping_panel() -> None:
        transform_config = form_state["data"].setdefault("transform", {})
        render_transform_mapping_config(transform_config)

    @ui.refreshable
    def transform_nonempty_panel() -> None:
        transform_config = form_state["data"].setdefault("transform", {})
        render_transform_nonempty_config(transform_config)

    @ui.refreshable
    def transform_duplicate_panel() -> None:
        transform_config = form_state["data"].setdefault("transform", {})
        render_transform_duplicate_config(transform_config)



    def render_transform():
        if not content_container:
            return

        content_container.clear()
        with content_container:
            ui.label("Transform").classes("text-2xl font-semibold")
            ui.separator()
            ui.label("Define transformations, mappings and validations.")

            # Transform configuration
            transform_config = form_state["data"].setdefault("transform", {})

            # General Transform Settings Card
            with ui.card().classes("mt-4"):
                ui.label("General Transform Settings").classes(
                    "text-lg font-semibold mb-3"
                )

                with ui.column().classes("gap-3"):
                    # Transform active toggle
                    def on_transform_active_change(e):
                        transform_config["active"] = e.value
                        mark_changed()

                    transform_active = transform_config.get("active", True)

                    ui.switch(
                        "Transform active",
                        value=transform_active,
                        on_change=on_transform_active_change,
                    ).tooltip(get_tip("transform.general.active"))
    # Load to Nemo toggle
                    def on_load_to_nemo_change(e):
                        transform_config["load_to_nemo"] = e.value
                        mark_changed()

                    load_to_nemo_switch = ui.switch(
                        "Load to Nemo",
                        value=transform_config.get("load_to_nemo", True),
                        on_change=on_load_to_nemo_change,
                    )
                    load_to_nemo_switch.tooltip(get_tip("transform.general.load_to_nemo"))


                    # Delete temp files toggle
                    def on_delete_temp_files_change(e):
                        transform_config["delete_temp_files"] = e.value
                        mark_changed()

                    load_to_nemo = transform_config.get("load_to_nemo", True)
                    delete_temp_files_switch = ui.switch(
                        "Delete temporary files",
                        value=transform_config.get("delete_temp_files", True),
                        on_change=on_delete_temp_files_change,
                    )
                    delete_temp_files_switch.tooltip(get_tip("transform.general.delete_temp_files"))


                    # Dump files toggle
                    def on_dump_files_change(e):
                        transform_config["dump_files"] = e.value
                        mark_changed()

                    dump_files_switch = ui.switch(
                        "Dump files",
                        value=transform_config.get("dump_files", True),
                        on_change=on_dump_files_change,
                    )
                    dump_files_switch.tooltip(get_tip("transform.general.dump_files"))


                    # Nemo project prefix
                    def on_nemo_project_prefix_change(e):
                        transform_config["nemo_project_prefix"] = e.value
                        mark_changed()

                    nemo_prefix_input = (
                        ui.input(
                            label="Nemo Project Prefix",
                            value=transform_config.get(
                                "nemo_project_prefix", "mmt"
                            ),
                            on_change=on_nemo_project_prefix_change,
                        )
                        .props("dense")
                        .classes("w-80").tooltip(get_tip("transform.general.nemo_project_prefix"))
                    )

            # Transform Components with Tabs
            with ui.card().classes("mt-4 w-5xl"):
                ui.label("Transform Components").classes(
                    "text-lg font-semibold mb-3"
                )

                transform_components_container = ui.column().classes("w-full")

                with ui.tabs().classes("w-full") as tabs:
                    ui.tab("Join")
                    ui.tab("Mapping")
                    ui.tab("Non-Empty")
                    ui.tab("Duplicate")

                with ui.tab_panels(tabs, value=transform_tab_state["value"]).classes("w-full") as panels:
                    panels.bind_value(transform_tab_state, "value")

                    with ui.tab_panel("Join").classes("w-full"):
                        transform_join_panel()

                    with ui.tab_panel("Mapping").classes("w-full"):
                        transform_mapping_panel()

                    with ui.tab_panel("Non-Empty").classes("w-full"):
                        transform_nonempty_panel()

                    with ui.tab_panel("Duplicate").classes("w-full"):
                        transform_duplicate_panel()

    def render_transform_join_config(transform_config):
        """Render join transformation configuration."""
        join_config = transform_config.setdefault("join", {})
        join_active = join_config.get("active", True)

        ui.label("Join Configuration").classes("text-lg font-semibold mb-3")
        ui.label("Configure table joins and relationships").classes(
            "text-sm mb-4"
        )

        with ui.column().classes("gap-4"):
            # Join active toggle
            def on_join_active_change(e):
                join_config["active"] = e.value
                mark_changed()
                # Re-render immediately so enable/disable state updates without tab switching
                transform_join_panel.refresh()

            with ui.row().classes("items-center gap-2"):
                join_active_switch = ui.switch(
                    "Join active",
                    value=join_active,
                    on_change=on_join_active_change,
                )
                render_folder_button(
                    resolve_join_dir(form_state),
                    get_tip("open_join_directory"),
                )
            join_active_switch.tooltip(get_tip("transform.join.active"))

            # Limit configuration
            def on_limit_change(e):
                try:
                    join_config["limit"] = int(e.value) if e.value else None
                    mark_changed()
                except ValueError:
                    pass

            limit_input = (
                ui.number(
                    label="Limit (optional)",
                    value=join_config.get("limit", None),
                    min=1,
                    placeholder="No limit",
                    on_change=on_limit_change,
                )
                .props("dense clearable")
                .classes("w-48 mt-3")
                .tooltip(get_tip("transform.join.limit"))
            )
            if not join_active:
                limit_input.props("disable")

            ui.label(
                "Set a limit on the number of rows to process during joins (leave empty for no limit)"
            ).classes("text-sm text-gray-600 mb-2")

            # Project-specific join configurations
            project_join_config_label = ui.label("Project Join Configurations").classes(
                "text-md font-medium mt-4"
            )
            if not join_active:
                project_join_config_label.classes("text-gray-500")

            ui.label(
                "Customize join configurations for individual projects"
            ).classes("text-sm text-gray-600 mb-2")

            # Get projects from setup configuration
            setup_config = form_state["data"].get("setup", {})
            projects = setup_config.get("projects", [])

            if not projects:
                ui.label("No projects configured in Setup").classes(
                    "text-sm text-gray-500 italic"
                )
            else:
                for project in projects:
                    with ui.card().classes("mt-2 p-3"):
                        with ui.row().classes(
                            "items-center justify-between w-full"
                        ):
                            project_label = ui.label(
                                f"Project: {project}"
                            ).classes("font-medium")
                            if not join_active:
                                project_label.classes("text-gray-500")

                            customize_button = ui.button(
                                "Customize",
                                icon="settings",
                                on_click=lambda proj=project: customize_project_join(
                                    proj
                                ),
                            ).props("flat size=sm color=primary").tooltip(get_tip("transform.join.customize_project"))
                            if not join_active:
                                customize_button.props("disable")

            def customize_project_join(project_name):
                """Customize join configuration for a specific project."""
                source_adapter = setup_config.get(
                    "source_adapter", "genericodbc"
                )
                filename = MigManUtils.getJoinFileName(project_name)

                indiv_file = (
                    Path(f"./config/{source_adapter}/joins/") / filename
                )
                if indiv_file.exists():
                    ui.notify(
                        f"Custom join file {indiv_file} already exists",
                        type="info",
                    )
                    return

                indiv_file.parent.mkdir(parents=True, exist_ok=True)

                default_file = (
                    importlib_resources.files("nemo_library_etl")
                    / "adapter"
                    / "migman"
                    / "config"
                    / "joins"
                    / source_adapter
                    / filename
                )

                # check for existence of the default file
                # if not default_file.exists():
                #     ui.notify(
                #         f"Default join configuration file {default_file} not found.",
                #         type="warning",
                #     )
                
                #     create empty indiv file
                #     indiv_file.touch()
                #     ui.notify(
                #         f"Created empty join configuration file at {indiv_file}",
                #         type="info",
                #     )
                #     return
                if not default_file.exists():
                    ui.notify(
                        f"Default join configuration file {default_file} not found.",
                        type="warning",
                    )

                    fields = MigManUtils.get_allowed_import_fields(project_name)
                    stub_sql = MigManUtils.generate_join_stub_sql(project_name, fields=fields)
                    indiv_file.write_text(stub_sql, encoding="utf-8")

                    if fields:
                        ui.notify(
                            f"Created join stub configuration file at {indiv_file} with {len(fields)} fields",
                            type="positive",
                        )
                    else:
                        ui.notify(
                            f"No allowed fields found for '{project_name}'. Placeholder SQL created at {indiv_file}",
                            type="warning",
                        )
                    return



                # copy default file to the indiv_file location
                shutil.copy(default_file, indiv_file)
                ui.notify(
                    f"Customized join file created at {indiv_file}",
                    type="positive",
                )

    def render_transform_mapping_config(transform_config):
        """Render mapping transformation configuration."""
        mapping_config = transform_config.setdefault("mapping", {})

        ui.label("Mapping Configuration").classes("text-lg font-semibold mb-3")
        ui.label("Configure field mappings and transformations").classes(
            "text-sm mb-4"
        )

        with ui.column().classes("gap-4"):
            # Mapping active toggle
            def on_mapping_active_change(e):
                mapping_config["active"] = e.value
                mark_changed()
                # Re-render immediately so enable/disable state updates without tab switching
                transform_mapping_panel.refresh()

            mapping_active = mapping_config.get("active", True)

            with ui.row().classes("items-center gap-2"):
                mapping_active_switch = ui.switch(
                    "Mapping active",
                    value=mapping_active,
                    on_change=on_mapping_active_change,
                )
                render_folder_button(
                    resolve_mapping_dir(form_state),
                    get_tip("open_mapping_directory"),
                )
            mapping_active_switch.tooltip(get_tip("transform.mapping.active"))
            ui.label(
                "This transformation will apply field mappings and data transformations according to the configured rules."
            ).classes("text-sm text-gray-600")

            # Mappings configuration
            mappings_config_label = ui.label("Field Mappings").classes(
                "text-md font-medium mt-4"
            )
            if not mapping_active:
                mappings_config_label.classes("text-gray-500")

            ui.label(
                "Create or update CSV templates for field mappings. ✅ indicates template exists, ⚪ indicates template not yet created."
            ).classes("text-sm text-gray-600 mb-2")

            mappings_config = mapping_config.setdefault("mappings", [])

            # Display existing mapping configurations
            for i, mapping_settings in enumerate(mappings_config):
                with ui.card().classes("mt-2 p-3"):
                    with ui.row().classes("items-center justify-between w-full"):
                        with ui.row().classes("items-center gap-2"):
                            mapping_label = ui.label(
                                f"Mapping {i+1}: {mapping_settings.get('field_name', 'Unnamed')}"
                            ).classes("font-medium")
                            if not mapping_active:
                                mapping_label.classes("text-gray-500")

                            # File existence check for display and button state
                            field_name = mapping_settings.get("field_name", "")
                            if field_name:
                                etl_dir = form_state["data"].get(
                                    "etl_directory", "./etl/migman"
                                )
                                mapping_dir = Path(etl_dir) / "mapping"
                                mapping_file = (
                                    mapping_dir
                                    / f"{MigManUtils.slugify_filename(field_name)}.csv"
                                )
                                file_exists = mapping_file.exists()
                                file_name = mapping_file.name
                                if file_exists:
                                    ui.icon("check_circle", size="sm").classes(
                                        "text-green-600"
                                    ).tooltip("Template file exists")
                                else:
                                    ui.icon(
                                        "radio_button_unchecked", size="sm"
                                    ).classes("text-gray-400").tooltip(
                                        "Template file not created yet"
                                    )
                            else:
                                file_exists = False
                                file_name = "template.csv"

                        with ui.row().classes("gap-2"):
                            # Create Template button
                            def create_template(idx=i):
                                field_name = mappings_config[idx].get(
                                    "field_name", ""
                                )
                                if not field_name:
                                    ui.notify(
                                        "Please set a field name before creating a template",
                                        type="warning",
                                    )
                                    return

                                # Create mapping directory if it doesn't exist
                                etl_dir = form_state["data"].get(
                                    "etl_directory", "./etl/migman"
                                )
                                mapping_dir = Path(etl_dir) / "mapping"
                                mapping_dir.mkdir(parents=True, exist_ok=True)

                                # Generate file path using the same logic as in transform_mapping.py
                                mapping_file = (
                                    mapping_dir
                                    / f"{MigManUtils.slugify_filename(field_name)}.csv"
                                )
                                def create_csv_template():
                                    """Create or extend the csv template file."""
                                    try:
                                        import pandas as pd

                                        source_values = _collect_distinct_mapping_values(field_name)

                                        if not source_values:
                                            ui.notify(
                                                f"No source values found for field '{field_name}' in join result tables",
                                                type="warning",
                                            )
                                            return

                                        new_df = pd.DataFrame(
                                            {
                                                "source_value": source_values,
                                                "target_value": [""] * len(source_values),
                                            }
                                        )

                                        appended_count = len(new_df)
                                        total_count = len(new_df)

                                        if mapping_file.exists():
                                            existing_df = pd.read_csv(
                                                mapping_file,
                                                sep=";",
                                                encoding="utf-8",
                                                dtype=str,
                                                keep_default_na=False,
                                            )

                                            if "source_value" not in existing_df.columns:
                                                ui.notify(
                                                    f"Existing file '{mapping_file.name}' has no column 'source_value'",
                                                    type="negative",
                                                )
                                                return

                                            if "target_value" not in existing_df.columns:
                                                existing_df["target_value"] = ""

                                            existing_df["source_value"] = (
                                                existing_df["source_value"].astype(str).fillna("").str.strip()
                                            )
                                            existing_df["target_value"] = (
                                                existing_df["target_value"].astype(str).fillna("")
                                            )

                                            existing_values = set(
                                                value
                                                for value in existing_df["source_value"].tolist()
                                                if value
                                            )

                                            rows_to_append = new_df[
                                                ~new_df["source_value"].isin(existing_values)
                                            ].copy()

                                            appended_count = len(rows_to_append)

                                            if appended_count > 0:
                                                final_df = pd.concat(
                                                    [existing_df[["source_value", "target_value"]], rows_to_append],
                                                    ignore_index=True,
                                                )
                                            else:
                                                final_df = existing_df[["source_value", "target_value"]].copy()

                                            total_count = len(final_df)
                                        else:
                                            final_df = new_df

                                        final_df.to_csv(
                                            mapping_file,
                                            index=False,
                                            sep=";",
                                            encoding="utf-8",
                                            quotechar='"',
                                            quoting=csv.QUOTE_ALL,
                                        )

                                        if mapping_file.exists() and appended_count >= 0:
                                            if appended_count == 0:
                                                ui.notify(
                                                    f"Template already up to date: {mapping_file.absolute()} ({total_count} values)",
                                                    type="positive",
                                                )
                                            else:
                                                ui.notify(
                                                    f"Template updated: {mapping_file.absolute()} (+{appended_count} new, total {total_count})",
                                                    type="positive",
                                                )
                                        else:
                                            ui.notify(
                                                f"Template created: {mapping_file.absolute()} ({total_count} values)",
                                                type="positive",
                                            )

                                        transform_mapping_panel.refresh()

                                    except FileNotFoundError as e:
                                        ui.notify(str(e), type="warning")
                                    except ImportError:
                                        ui.notify(
                                            "pandas library not available for csv creation",
                                            type="negative",
                                        )
                                    except Exception as e:
                                        ui.notify(
                                            f"Error creating template: {str(e)}",
                                            type="negative",
                                        )
                                # Check if file already exists
                                if mapping_file.exists():
                                    # Show confirmation dialog for template update
                                    def confirm_override():
                                        async def on_confirm():
                                            override_dialog.close()
                                            create_csv_template()

                                        async def on_cancel():
                                            override_dialog.close()

                                        with ui.dialog() as override_dialog, ui.card():
                                            ui.label(
                                                f"File already exists: {mapping_file.name}"
                                            ).classes(
                                                "text-lg font-semibold mb-3"
                                            )
                                            ui.label(
                                                "Do you want to update the existing file and append missing source values?"
                                            ).classes("mb-4")

                                            with ui.row().classes(
                                                "gap-2 justify-end"
                                            ):
                                                ui.button(
                                                    "Cancel", on_click=on_cancel
                                                ).props("flat")
                                                ui.button(
                                                    "Update",
                                                    on_click=on_confirm,
                                                ).props("color=primary")

                                        override_dialog.open()

                                    confirm_override()
                                else:
                                    create_csv_template()

                            # Create Template button with existence indicator
                            if file_exists:
                                button_icon = "file_present"
                                button_tooltip = f"Template exists: {file_name} (click to override)"
                                button_color = "orange"
                            else:
                                button_icon = "create_new_folder"
                                button_tooltip = "Create CSV template"
                                button_color = "primary"

                            create_template_button = (
                                ui.button(
                                    icon=button_icon,
                                    on_click=lambda e, idx=i: create_template(
                                        idx
                                    ),
                                )
                                .props(
                                    f"flat round color={button_color} size=sm"
                                ).tooltip(get_tip("transform.mapping.create_template"))
                            )
                            if not mapping_active:
                                create_template_button.props("disable")

                            delete_button = ui.button(
                                icon="delete",
                                on_click=lambda idx=i: remove_mapping_config(
                                    idx
                                ),
                            ).props("flat round color=negative size=sm")
                            if not mapping_active:
                                delete_button.props("disable")

                    def on_mapping_item_active_change(e, idx=i):
                        mappings_config[idx]["active"] = e.value
                        mark_changed()

                    def on_field_name_change(e, idx=i):
                        mappings_config[idx]["field_name"] = e.value
                        mark_changed()
                        # Re-render to update the label
                        transform_mapping_panel.refresh()

                    mapping_switch = ui.switch(
                        "Active",
                        value=mapping_settings.get("active", True),
                        on_change=lambda e, idx=i: on_mapping_item_active_change(
                            e, idx
                        ),
                    )
                    if not mapping_active:
                        mapping_switch.props("disable")

                    field_name_input = (
                        ui.input(
                            label="Field Name",
                            value=mapping_settings.get("field_name", ""),
                            placeholder="Enter field name for mapping",
                            on_change=lambda e, idx=i: on_field_name_change(
                                e, idx
                            ),
                        )
                        .props("dense")
                        .classes("w-full mt-2")
                        .tooltip(get_tip("transform.mapping.field_name"))
                    )
                    if not mapping_active:
                        field_name_input.props("disable")

            def remove_mapping_config(idx):
                if 0 <= idx < len(mappings_config):
                    del mappings_config[idx]
                    mark_changed()
                    transform_mapping_panel.refresh()

            # Add new mapping configuration (always visible; disabled when mapping is inactive)
            with ui.card().classes("mt-2 p-3 border-dashed"):
                ui.label("Add New Field Mapping").classes(
                    "font-medium mb-2"
                )

                new_field_name = {"value": ""}

                def on_new_field_name_change(e):
                    new_field_name["value"] = e.value

                def add_mapping_config():
                    if not mapping_active:
                        return
                    if new_field_name["value"]:
                        mappings_config.append(
                            {
                                "active": True,
                                "field_name": new_field_name["value"],
                            }
                        )
                        mark_changed()
                        transform_mapping_panel.refresh()

                with ui.column().classes("gap-2 w-full"):
                    new_map_input = ui.input(
                        label="Field Name",
                        placeholder="Enter field name for mapping",
                        on_change=on_new_field_name_change,
                    ).props("dense").classes("w-full").tooltip(get_tip("transform.mapping.field_name"))
                    if not mapping_active:
                        new_map_input.props("disable")

                    add_map_btn = ui.button(
                        "Add Mapping",
                        icon="add",
                        on_click=add_mapping_config,
                    ).props("color=primary")
                    if not mapping_active:
                        add_map_btn.props("disable")

            # Synonyms configuration
            synonyms_config_label = ui.label("Field Synonyms").classes(
                "text-md font-medium mt-6"
            )
            if not mapping_active:
                synonyms_config_label.classes("text-gray-500")

            ui.label(
                "Configure field synonyms to map multiple fields to a single source field."
            ).classes("text-sm text-gray-600")

            synonyms_config = mapping_config.setdefault("synonyms", [])

            # Display existing synonym configurations
            for i, synonym_settings in enumerate(synonyms_config):
                with ui.card().classes("mt-2 p-3"):
                    with ui.row().classes(
                        "items-center justify-between w-full"
                    ):
                        synonym_label = ui.label(
                            f"Synonym {i+1}: {synonym_settings.get('source_field', 'Unnamed')}"
                        ).classes("font-medium")
                        if not mapping_active:
                            synonym_label.classes("text-gray-500")

                        delete_button = ui.button(
                            icon="delete",
                            on_click=lambda idx=i: remove_synonym_config(idx),
                        ).props("flat round color=negative size=sm")
                        if not mapping_active:
                            delete_button.props("disable")

                    def on_source_field_change(e, idx=i):
                        synonyms_config[idx]["source_field"] = e.value
                        mark_changed()
                        transform_mapping_panel.refresh()

                    def on_synonym_fields_change(e, idx=i):
                        synonyms_config[idx]["synonym_fields"] = e.value
                        mark_changed()

                    source_field_input = (
                        ui.input(
                            label="Source Field",
                            value=synonym_settings.get("source_field", ""),
                            placeholder="e.g., S_Kunde.Kunde",
                            on_change=lambda e, idx=i: on_source_field_change(
                                e, idx
                            ),
                        )
                        .props("dense")
                        .classes("w-full mt-2")
                        .tooltip(get_tip("transform.mapping.source_field"))
                    )
                    if not mapping_active:
                        source_field_input.props("disable")

                    # Get current synonym fields and all available fields from default config
                    current_synonym_fields = synonym_settings.get(
                        "synonym_fields", []
                    )

                    # Collect all possible field options from the default synonyms
                    all_synonym_options = set()
                    default_synonyms = (
                        global_config.get("transform", {})
                        .get("mapping", {})
                        .get("synonyms", [])
                    )
                    for default_syn in default_synonyms:
                        all_synonym_options.add(
                            default_syn.get("source_field", "")
                        )
                        all_synonym_options.update(
                            default_syn.get("synonym_fields", [])
                        )

                    # Add current fields that might not be in defaults
                    all_synonym_options.update(current_synonym_fields)

                    # Remove empty strings and sort
                    all_synonym_options = sorted(
                        [field for field in all_synonym_options if field]
                    )

                    synonym_fields_select = (
                        ui.select(
                            options=all_synonym_options,
                            value=current_synonym_fields,
                            multiple=True,
                            label="Synonym Fields",
                            on_change=lambda e, idx=i: on_synonym_fields_change(
                                e, idx
                            ),
                        )
                        .props("use-chips clearable use-input input-debounce=0")
                        .classes("w-full mt-2")
                        .tooltip(get_tip("transform.mapping.synonym_fields"))
                    )
                    if not mapping_active:
                        synonym_fields_select.props("disable")

                    # Add custom field functionality for synonyms
                    with ui.row().classes("gap-2 items-end mt-2"):
                        new_synonym_field = {"value": ""}

                        def on_new_synonym_field_change(e, idx=i):
                            new_synonym_field["value"] = e.value

                        def add_custom_synonym_field(idx=i):
                            if not mapping_active:
                                return
                            if (
                                new_synonym_field["value"]
                                and new_synonym_field["value"]
                                not in current_synonym_fields
                            ):
                                synonyms_config[idx]["synonym_fields"].append(
                                    new_synonym_field["value"]
                                )
                                mark_changed()
                                transform_mapping_panel.refresh()

                        custom_field_input = (
                            ui.input(
                                label="Add custom field",
                                placeholder="e.g., MyTable.MyField",
                                on_change=lambda e, idx=i: on_new_synonym_field_change(
                                    e, idx
                                ),
                            )
                            .props("dense")
                            .classes("flex-1")
                        )
                        if not mapping_active:
                            custom_field_input.props("disable")

                        add_field_button = ui.button(
                            "Add",
                            icon="add",
                            on_click=lambda e, idx=i: add_custom_synonym_field(
                                idx
                            ),
                        ).props("dense")
                        if not mapping_active:
                            add_field_button.props("disable")

            def remove_synonym_config(idx):
                if 0 <= idx < len(synonyms_config):
                    del synonyms_config[idx]
                    mark_changed()
                    transform_mapping_panel.refresh()

            # Add new synonym configuration (always visible; disabled when mapping is inactive)
            with ui.card().classes("mt-2 p-3 border-dashed"):
                ui.label("Add New Field Synonym").classes(
                    "font-medium mb-2"
                )

                new_source_field = {"value": ""}
                new_synonym_fields = {"value": []}

                def on_new_source_field_change(e):
                    new_source_field["value"] = e.value

                def on_new_synonym_fields_change(e):
                    new_synonym_fields["value"] = e.value

                def add_synonym_config():
                    if not mapping_active:
                        return
                    if (
                        new_source_field["value"]
                        and new_synonym_fields["value"]
                    ):
                        synonyms_config.append(
                            {
                                "source_field": new_source_field["value"],
                                "synonym_fields": new_synonym_fields[
                                    "value"
                                ],
                            }
                        )
                        mark_changed()
                        transform_mapping_panel.refresh()

                # Get all available field options from default config for the new form
                all_synonym_options = set()
                default_synonyms = (
                    global_config.get("transform", {})
                    .get("mapping", {})
                    .get("synonyms", [])
                )
                for default_syn in default_synonyms:
                    all_synonym_options.add(
                        default_syn.get("source_field", "")
                    )
                    all_synonym_options.update(
                        default_syn.get("synonym_fields", [])
                    )

                # Remove empty strings and sort
                all_synonym_options = sorted(
                    [field for field in all_synonym_options if field]
                )

                with ui.column().classes("gap-2 w-full"):
                    new_source_input = ui.input(
                        label="Source Field",
                        placeholder="e.g., S_Kunde.Kunde",
                        on_change=on_new_source_field_change,
                    ).props("dense").classes("w-full").tooltip(get_tip("transform.mapping.source_field"))
                    if not mapping_active:
                        new_source_input.props("disable")

                    synonym_fields_select = (
                        ui.select(
                            options=all_synonym_options,
                            value=[],
                            multiple=True,
                            label="Synonym Fields",
                            on_change=on_new_synonym_fields_change,
                        )
                        .props(
                            "use-chips clearable use-input input-debounce=0"
                        )
                        .classes("w-full")
                        .tooltip(get_tip("transform.mapping.synonym_fields"))
                    )
                    if not mapping_active:
                        synonym_fields_select.props("disable")

                    # Add custom field functionality for new synonym
                    with ui.row().classes("gap-2 items-end"):
                        new_custom_field = {"value": ""}

                        def on_new_custom_field_change(e):
                            new_custom_field["value"] = e.value

                        def add_custom_field_to_new():
                            if not mapping_active:
                                return
                            if (
                                new_custom_field["value"]
                                and new_custom_field["value"]
                                not in new_synonym_fields["value"]
                            ):
                                current_fields = new_synonym_fields[
                                    "value"
                                ].copy()
                                current_fields.append(
                                    new_custom_field["value"]
                                )
                                new_synonym_fields["value"] = current_fields
                                transform_mapping_panel.refresh()

                        new_custom_field_input = (
                            ui.input(
                                label="Add custom field",
                                placeholder="e.g., MyTable.MyField",
                                on_change=on_new_custom_field_change,
                            )
                            .props("dense")
                            .classes("flex-1")
                        )
                        if not mapping_active:
                            new_custom_field_input.props("disable")

                        add_custom_btn = ui.button(
                            "Add Field",
                            icon="add",
                            on_click=lambda e: add_custom_field_to_new(),
                        ).props("dense")
                        if not mapping_active:
                            add_custom_btn.props("disable")

                    add_syn_btn = ui.button(
                        "Add Synonym",
                        icon="add",
                        on_click=add_synonym_config,
                    ).props("color=primary")
                    if not mapping_active:
                        add_syn_btn.props("disable")
    def render_transform_nonempty_config(transform_config):
        """Render non-empty transformation configuration."""
        nonempty_config = transform_config.setdefault("nonempty", {})

        ui.label("Non-Empty Configuration").classes(
            "text-lg font-semibold mb-3"
        )
        ui.label("Filter out empty columns during transformation").classes(
            "text-sm mb-4"
        )

        with ui.column().classes("gap-4"):
            # Non-empty active toggle
            def on_nonempty_active_change(e):
                nonempty_config["active"] = e.value
                mark_changed()

            ui.switch(
                "Non-empty filter active",
                value=nonempty_config.get("active", True),
                on_change=on_nonempty_active_change,
            ).tooltip(get_tip("transform.nonempty.active"))

            ui.label(
                "This transformation will remove columns with empty or null values."
            ).classes("text-sm text-gray-600")

    def render_transform_duplicate_config(transform_config):
        """Render duplicate transformation configuration."""
        duplicate_config = transform_config.setdefault("duplicate", {})

        ui.label("Duplicate Configuration").classes(
            "text-lg font-semibold mb-3"
        )
        ui.label("Configure duplicate detection and handling").classes(
            "text-sm mb-4"
        )

        duplicate_active = duplicate_config.get("active", True)

        with ui.column().classes("gap-4 w-full"):
            # Duplicate active toggle
            def on_duplicate_active_change(e):
                duplicate_config["active"] = e.value
                mark_changed()
                # Re-render immediately so enable/disable state updates without tab switching
                transform_duplicate_panel.refresh()

            ui.switch(
                "Duplicate detection active",
                value=duplicate_active,
                on_change=on_duplicate_active_change,
            ).tooltip(get_tip("transform.duplicate.active"))
    # Duplicates configuration
            duplicates_config_label = ui.label(
                "Duplicate Configurations"
            ).classes("text-md font-medium mt-4")
            if not duplicate_active:
                duplicates_config_label.classes("text-gray-500")

            duplicates_config = duplicate_config.setdefault("duplicates", {})

            # Display existing duplicate configurations
            for object_name, duplicate_settings in duplicates_config.items():
                with ui.card().classes("mt-2 p-3 w-full") as duplicate_card:

                    with ui.row().classes(
                        "items-center justify-between w-full"
                    ):
                        object_label = ui.label(
                            f"Object: {object_name}"
                        ).classes("font-medium")
                        if not duplicate_active:
                            object_label.classes("text-gray-500")

                        delete_button = ui.button(
                            icon="delete",
                            on_click=lambda name=object_name: remove_duplicate_config(
                                name
                            ),
                        ).props("flat round color=negative size=sm")
                        if not duplicate_active:
                            delete_button.props("disable")

                    def on_duplicate_obj_active_change(e, name=object_name):
                        duplicates_config[name]["active"] = e.value
                        mark_changed()

                    def on_threshold_change(e, name=object_name):
                        try:
                            threshold = int(e.value)
                            if 0 <= threshold <= 100:
                                duplicates_config[name]["threshold"] = threshold
                                mark_changed()
                        except (ValueError, TypeError):
                            pass

                    def on_primary_key_change(e, name=object_name):
                        duplicates_config[name]["primary_key"] = e.value
                        mark_changed()

                    def on_fields_select_change(e, name=object_name):
                        duplicates_config[name]["fields"] = e.value or []
                        mark_changed()

                    duplicate_obj_switch = ui.switch(
                        "Active",
                        value=duplicate_settings.get("active", True),
                        on_change=lambda e, name=object_name: on_duplicate_obj_active_change(
                            e, name
                        ),
                    )
                    duplicate_obj_switch.tooltip(get_tip("transform.duplicate.object_active"))
                    if not duplicate_active:
                        duplicate_obj_switch.props("disable")

                    with ui.row().classes("gap-2 w-full"):
                        threshold_input = (
                            ui.number(
                                label="Similarity Threshold (%)",
                                value=duplicate_settings.get("threshold", 90),
                                min=0,
                                max=100,
                                on_change=lambda e, name=object_name: on_threshold_change(
                                    e, name
                                ),
                            )
                            .props("dense")
                            .classes("w-48")
                            .tooltip(get_tip("transform.duplicate.threshold"))
                        )
                        if not duplicate_active:
                            threshold_input.props("disable")

                        primary_key_input = (
                            ui.input(
                                label="Primary Key",
                                value=duplicate_settings.get("primary_key", ""),
                                placeholder="Enter primary key field",
                                on_change=lambda e, name=object_name: on_primary_key_change(
                                    e, name
                                ),
                            )
                            .props("dense")
                            .classes("flex-1").tooltip(get_tip("transform.duplicate.primary_key"))
                        )
                        if not duplicate_active:
                            primary_key_input.props("disable")

                    # Get current duplicate fields and available field options from the default configuration
                    current_duplicate_fields = duplicate_settings.get("fields", [])

                    default_duplicates = (
                        global_config.get("transform", {})
                        .get("duplicate", {})
                        .get("duplicates", {})
                    )
                    default_fields = default_duplicates.get(object_name, {}).get("fields", [])

                    all_field_options = sorted({f for f in (default_fields + current_duplicate_fields) if f})

                    fields_select = (
                        ui.select(
                            options=all_field_options,
                            value=current_duplicate_fields,
                            multiple=True,
                            label="Fields",
                            on_change=lambda e, name=object_name: on_fields_select_change(e, name),
                        )
                        .props("use-chips clearable use-input input-debounce=0")
                        .classes("w-full")
                        .tooltip(get_tip("transform.duplicate.fields"))
                    )
                    if not duplicate_active:
                        fields_select.props("disable")

                    # Add custom field functionality (lets you extend beyond defaults from the UI)
                    with ui.row().classes("gap-2 items-end mt-2"):
                        custom_field_model = {"value": ""}

                        custom_field_input = (
                            ui.input(
                                label="Add custom field",
                                placeholder="e.g., S_Adresse.Email",
                            )
                            .props("dense")
                            .classes("flex-1")
                            .bind_value(custom_field_model, "value").tooltip(get_tip("transform.duplicate.add_custom_field"))
    )
                        if not duplicate_active:
                            custom_field_input.props("disable")

                        # Make sure the input value is synchronized *while typing* (no blur/tab change required).
                        def _sync_custom_field_value(e, model=custom_field_model):
                            val = getattr(e, "value", None)
                            if val is None:
                                args = getattr(e, "args", None)
                                if isinstance(args, dict) and "value" in args:
                                    val = args.get("value")
                                else:
                                    val = args
                            model["value"] = "" if val is None else str(val)

                        custom_field_input.on("input", _sync_custom_field_value)
                        custom_field_input.on("update:model-value", _sync_custom_field_value)

                        def add_custom_field(
                            e=None,
                            name=object_name,
                            model=custom_field_model,
                            input_el=custom_field_input,
                        ):
                            if not duplicate_active:
                                return
                            raw = (model.get("value") or input_el.value or "").strip()
                            if not raw:
                                return

                            values = [v.strip() for v in raw.split(",") if v.strip()]

                            fields = duplicates_config[name].setdefault("fields", [])
                            changed = False

                            for value in values:
                                if value not in fields:
                                    fields.append(value)
                                    changed = True

                            if changed:
                                mark_changed()
                                transform_duplicate_panel.refresh()

                            model["value"] = ""
                            input_el.value = ""

                        add_custom_btn = ui.button(
                            "Add",
                            icon="add",
                            on_click=add_custom_field,
                        ).props("dense").tooltip(get_tip("transform.duplicate.add_custom_field_apply"))
                        if not duplicate_active:
                            add_custom_btn.props("disable")

            def remove_duplicate_config(object_name):
                if object_name in duplicates_config:
                    del duplicates_config[object_name]
                    mark_changed()
                    transform_duplicate_panel.refresh()

            # Add new duplicate configuration (always visible; disabled when duplicate detection is inactive)
            add_duplicate_card = ui.card().classes("mt-2 p-3 border-dashed w-full")
            with add_duplicate_card:
                ui.label("Add New Duplicate Configuration").classes(
                    "font-medium mb-2"
                )

                new_object_name = {"value": ""}
                new_primary_key = {"value": ""}
                new_threshold = {"value": 90}
                new_fields = {"value": []}

                def on_new_object_name_change(e):
                    new_object_name["value"] = e.value

                def on_new_primary_key_change(e):
                    new_primary_key["value"] = e.value

                def on_new_threshold_change(e):
                    try:
                        threshold = int(e.value)
                        if 0 <= threshold <= 100:
                            new_threshold["value"] = threshold
                    except (ValueError, TypeError):
                        pass

                def on_new_fields_change(e):
                    new_fields["value"] = e.value or []

                def add_duplicate_config():
                    if not duplicate_active:
                        return
                    if new_object_name["value"] and new_primary_key["value"]:
                        duplicates_config[new_object_name["value"]] = {
                            "active": True,
                            "threshold": new_threshold["value"],
                            "primary_key": new_primary_key["value"],
                            "fields": new_fields["value"] or [],
                        }

                        new_object_name["value"] = ""
                        new_primary_key["value"] = ""
                        new_threshold["value"] = 90
                        new_fields["value"] = []

                        mark_changed()
                        transform_duplicate_panel.refresh()

                with ui.column().classes("gap-2 w-full"):
                    with ui.row().classes("gap-2 w-full"):
                        object_name_input = (
                            ui.input(
                                label="Object Name",
                                value=new_object_name["value"],
                                placeholder="Enter object name",
                                on_change=on_new_object_name_change,
                            )
                            .props("dense")
                            .classes("flex-1")
                            .tooltip(get_tip("transform.duplicate.object_name"))
                        )
                        threshold_input = (
                            ui.number(
                                label="Threshold (%)",
                                value=new_threshold["value"],
                                min=0,
                                max=100,
                                on_change=on_new_threshold_change,
                            )
                            .props("dense")
                            .classes("w-32")
                            .tooltip(get_tip("transform.duplicate.threshold"))
                        )
                        if not duplicate_active:
                            object_name_input.props("disable")
                            threshold_input.props("disable")

                    with ui.row().classes("gap-2 w-full"):
                        primary_key_input = (
                            ui.input(
                                label="Primary Key",
                                value=new_primary_key["value"],
                                placeholder="Enter primary key field",
                                on_change=on_new_primary_key_change,
                            )
                            .props("dense")
                            .classes("flex-1")
                            .tooltip(get_tip("transform.duplicate.primary_key"))
                        )
                        if not duplicate_active:
                            primary_key_input.props("disable")

                        default_duplicates = (
                            global_config.get("transform", {})
                            .get("duplicate", {})
                            .get("duplicates", {})
                        )
                        new_field_options = set()
                        for default_obj in default_duplicates.values():
                            new_field_options.update(default_obj.get("fields", []))

                        new_field_options.update(new_fields["value"])
                        new_field_options = sorted([f for f in new_field_options if f])

                        fields_select = (
                            ui.select(
                                options=new_field_options,
                                value=new_fields["value"],
                                multiple=True,
                                label="Fields",
                                on_change=on_new_fields_change,
                            )
                            .props("use-chips clearable use-input input-debounce=0")
                            .classes("flex-1")
                            .tooltip(get_tip("transform.duplicate.fields"))
                        )
                        if not duplicate_active:
                            fields_select.props("disable")

                    with ui.row().classes("gap-2 items-end mt-2 w-full"):
                        new_custom_field = {"value": ""}

                        def on_new_custom_field_change(e):
                            new_custom_field["value"] = e.value

                        def add_custom_new_field():
                            if not duplicate_active:
                                return
                            value = (new_custom_field["value"] or "").strip()
                            if not value:
                                return

                            if value not in new_fields["value"]:
                                new_fields["value"].append(value)
                                mark_changed()
                                new_custom_field["value"] = ""
                                transform_duplicate_panel.refresh()

                        new_custom_input = ui.input(
                            label="Add custom field",
                            placeholder="e.g., S_Adresse.Email",
                        ).props("dense").classes("flex-1").bind_value(new_custom_field, "value").tooltip(get_tip("transform.duplicate.add_custom_field"))
                        if not duplicate_active:
                            new_custom_input.props("disable")

                        new_custom_btn = ui.button(
                            "Add",
                            icon="add",
                            on_click=lambda e: add_custom_new_field(),
                        ).props("dense").tooltip(get_tip("transform.duplicate.add_custom_field_apply"))
                        if not duplicate_active:
                            new_custom_btn.props("disable")

                    add_button = ui.button(
                        "Add Configuration",
                        icon="add",
                        on_click=add_duplicate_config,
                    ).props("color=primary")
                    if not duplicate_active:
                        add_button.props("disable")

    render_transform()
