// src/services/ui_service.rs

use crate::services::grpc_client::GrpcClient;
use crate::state::app::state::AppState;
use crate::state::pages::add_logic::AddLogicState;
use crate::state::pages::form::{FieldDefinition, FormState};
use crate::tui::functions::common::form::SaveOutcome;
use crate::utils::columns::filter_user_columns;
use anyhow::{anyhow, Context, Result};
use std::sync::Arc;

pub struct UiService;

impl UiService {
    pub async fn load_table_view(
        grpc_client: &mut GrpcClient,
        app_state: &mut AppState,
        profile_name: &str,
        table_name: &str,
    ) -> Result<FormState> {
        // 1. & 2. Fetch and Cache Schema - UNCHANGED
        let table_structure = grpc_client
            .get_table_structure(profile_name.to_string(), table_name.to_string())
            .await
            .context(format!(
                "Failed to get table structure for {}.{}",
                profile_name, table_name
            ))?;
        let cache_key = format!("{}.{}", profile_name, table_name);
        app_state
            .schema_cache
            .insert(cache_key, Arc::new(table_structure.clone()));
        tracing::info!("Schema for '{}.{}' cached.", profile_name, table_name);

        // --- START: FINAL, SIMPLIFIED, CORRECT LOGIC ---

        // 3a. Create definitions for REGULAR fields first.
        let mut fields: Vec<FieldDefinition> = table_structure
            .columns
            .iter()
            .filter(|col| {
                !col.is_primary_key
                    && col.name != "deleted"
                    && col.name != "created_at"
                    && !col.name.ends_with("_id") // Filter out ALL potential links
            })
            .map(|col| FieldDefinition {
                display_name: col.name.clone(),
                data_key: col.name.clone(),
                is_link: false,
                link_target_table: None,
            })
            .collect();

        // 3b. Now, find and APPEND definitions for LINK fields based on the `_id` convention.
        let link_fields: Vec<FieldDefinition> = table_structure
            .columns
            .iter()
            .filter(|col| col.name.ends_with("_id")) // Find all foreign key columns
            .map(|col| {
                // The table we link to is derived from the column name.
                // e.g., "test_diacritics_id" -> "test_diacritics"
                let target_table_base = col
                    .name
                    .strip_suffix("_id")
                    .unwrap_or(&col.name);

                // Find the full table name from the profile tree for display.
                // e.g., "test_diacritics" -> "2025_test_diacritics"
                let full_target_table_name = app_state
                    .profile_tree
                    .profiles
                    .iter()
                    .find(|p| p.name == profile_name)
                    .and_then(|p| p.tables.iter().find(|t| t.name.ends_with(target_table_base)))
                    .map_or(target_table_base.to_string(), |t| t.name.clone());

                FieldDefinition {
                    display_name: full_target_table_name.clone(),
                    data_key: col.name.clone(), // The actual FK column name
                    is_link: true,
                    link_target_table: Some(full_target_table_name),
                }
            })
            .collect();

        fields.extend(link_fields); // Append the link fields to the end

        // --- END: FINAL, SIMPLIFIED, CORRECT LOGIC ---

        Ok(FormState::new(
            profile_name.to_string(),
            table_name.to_string(),
            fields,
        ))
    }

    pub async fn initialize_add_logic_table_data(
        grpc_client: &mut GrpcClient,
        add_logic_state: &mut AddLogicState,
        profile_tree: &common::proto::komp_ac::table_definition::ProfileTreeResponse,
    ) -> Result<String> {
        let profile_name_clone_opt = Some(add_logic_state.profile_name.clone());
        let table_name_opt_clone = add_logic_state.selected_table_name.clone();

        // Collect table names from SAME profile only
        let same_profile_table_names: Vec<String> = profile_tree.profiles
            .iter()
            .find(|profile| profile.name == add_logic_state.profile_name)
            .map(|profile| profile.tables.iter().map(|table| table.name.clone()).collect())
            .unwrap_or_default();

        // Set same profile table names for autocomplete
        add_logic_state.set_same_profile_table_names(same_profile_table_names.clone());

        if let (Some(profile_name_clone), Some(table_name_clone)) = (profile_name_clone_opt, table_name_opt_clone) {
            match grpc_client.get_table_structure(profile_name_clone.clone(), table_name_clone.clone()).await {
                Ok(response) => {
                    let column_names: Vec<String> = response.columns
                        .into_iter()
                        .map(|col| col.name)
                        .collect();

                    add_logic_state.set_table_columns(column_names.clone());

                    Ok(format!(
                        "Loaded {} columns for table '{}' and {} tables from profile '{}'",
                        column_names.len(),
                        table_name_clone,
                        same_profile_table_names.len(),
                        add_logic_state.profile_name
                    ))
                }
                Err(e) => {
                    tracing::warn!(
                        "Failed to fetch table structure for {}.{}: {}",
                        profile_name_clone,
                        table_name_clone,
                        e
                    );
                    Ok(format!(
                        "Warning: Could not load table structure for '{}'. Autocomplete will use {} tables from profile '{}'.",
                        table_name_clone,
                        same_profile_table_names.len(),
                        add_logic_state.profile_name
                    ))
                }
            }
        } else {
            Ok(format!(
                "No table selected for Add Logic. Loaded {} tables from profile '{}' for autocomplete.",
                same_profile_table_names.len(),
                add_logic_state.profile_name
            ))
        }
    }

    /// Fetches columns for a specific table (used for table.column autocomplete)
    pub async fn fetch_columns_for_table(
        grpc_client: &mut GrpcClient,
        profile_name: &str,
        table_name: &str,
    ) -> Result<Vec<String>> {
        match grpc_client.get_table_structure(profile_name.to_string(), table_name.to_string()).await {
            Ok(response) => {
                let column_names: Vec<String> = response.columns
                    .into_iter()
                    .map(|col| col.name)
                    .collect();
                Ok(filter_user_columns(column_names))
            }
            Err(e) => {
                tracing::warn!("Failed to fetch columns for {}.{}: {}", profile_name, table_name, e);
                Err(e.into())
            }
        }
    }

    // TODO REFACTOR (maybe)
    pub async fn initialize_app_state_and_form(
        grpc_client: &mut GrpcClient,
        app_state: &mut AppState,
    ) -> Result<(String, String, Vec<String>)> {
        let profile_tree = grpc_client
            .get_profile_tree()
            .await
            .context("Failed to get profile tree")?;

        app_state.profile_tree = profile_tree;

        // Find first profile that contains tables
        let (initial_profile_name, initial_table_name) = app_state
            .profile_tree
            .profiles
            .iter()
            .find(|profile| !profile.tables.is_empty())
            .and_then(|profile| {
                profile.tables.first().map(|table| {
                    (profile.name.clone(), table.name.clone())
                })
            })
            .ok_or_else(|| anyhow!("No profiles with tables found. Create a table first."))?;

        app_state.set_current_view_table(
            initial_profile_name.clone(),
            initial_table_name.clone(),
        );

        let form_state = Self::load_table_view(
            grpc_client,
            app_state,
            &initial_profile_name,
            &initial_table_name,
        )
        .await?;

        let field_names = form_state.fields.iter().map(|f| f.display_name.clone()).collect();

        Ok((initial_profile_name, initial_table_name, field_names))
    }

    pub async fn fetch_and_set_table_count(
        grpc_client: &mut GrpcClient,
        form_state: &mut FormState,
    ) -> Result<()> {
        let total_count = grpc_client
            .get_table_data_count(
                form_state.profile_name.clone(),
                form_state.table_name.clone(),
            )
            .await
            .context(format!(
                "Failed to get count for table {}.{}",
                form_state.profile_name, form_state.table_name
            ))?;
        form_state.total_count = total_count;

        if total_count > 0 {
            form_state.current_position = total_count;
        } else {
            form_state.current_position = 1;
        }
        Ok(())
    }

    pub async fn load_table_data_by_position(
        grpc_client: &mut GrpcClient,
        form_state: &mut FormState,
    ) -> Result<String> {
        if form_state.current_position == 0 || (form_state.total_count > 0 && form_state.current_position > form_state.total_count) {
            form_state.reset_to_empty();
            return Ok(format!(
                "New entry mode for table {}.{}",
                form_state.profile_name, form_state.table_name
            ));
        }
        if form_state.total_count == 0 && form_state.current_position == 1 {
            form_state.reset_to_empty();
            return Ok(format!(
                "New entry mode for empty table {}.{}",
                form_state.profile_name, form_state.table_name
            ));
        }

        match grpc_client
            .get_table_data_by_position(
                form_state.profile_name.clone(),
                form_state.table_name.clone(),
                form_state.current_position as i32,
            )
            .await
        {
            Ok(response) => {
                // FIX: Pass the current position as the second argument
                form_state.update_from_response(&response.data, form_state.current_position);
                Ok(format!(
                    "Loaded entry {}/{} for table {}.{}",
                    form_state.current_position,
                    form_state.total_count,
                    form_state.profile_name,
                    form_state.table_name
                ))
            }
            Err(e) => {
                tracing::error!(
                    "Error loading entry {} for table {}.{}: {}",
                    form_state.current_position,
                    form_state.profile_name,
                    form_state.table_name,
                    e
                );
                Err(anyhow::anyhow!(
                    "Error loading entry {}: {}",
                    form_state.current_position,
                    e
                ))
            }
        }
    }

    pub async fn handle_save_outcome(
        save_outcome: SaveOutcome,
        _grpc_client: &mut GrpcClient,
        _app_state: &mut AppState,
        form_state: &mut FormState,
    ) -> Result<()> {
        match save_outcome {
            SaveOutcome::CreatedNew(new_id) => {
                form_state.id = new_id;
            }
            SaveOutcome::UpdatedExisting | SaveOutcome::NoChange => {
                // No action needed
            }
        }
        Ok(())
    }
}
