// src/pages/forms/logic.rs
use crate::services::grpc_client::GrpcClient;
use crate::state::app::state::AppState;
use crate::pages::forms::FormState;
use crate::utils::data_converter;
use anyhow::{anyhow, Context, Result};
use std::collections::HashMap;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SaveOutcome {
    NoChange,
    UpdatedExisting,
    CreatedNew(i64),
}

pub async fn save(
    app_state: &mut AppState,
    path: &str,
    grpc_client: &mut GrpcClient,
) -> Result<SaveOutcome> {
    if let Some(fs) = app_state.form_state_for_path(path) {
        if !fs.has_unsaved_changes {
            return Ok(SaveOutcome::NoChange);
        }

        let profile_name = fs.profile_name.clone();
        let table_name = fs.table_name.clone();
        let fields = fs.fields.clone();
        let values = fs.values.clone();
        let id = fs.id;
        let total_count = fs.total_count;
        let current_position = fs.current_position;

        let cache_key = format!("{}.{}", profile_name, table_name);
        let schema = app_state
            .schema_cache
            .get(&cache_key)
            .ok_or_else(|| {
                anyhow!(
                    "Schema for table '{}' not found in cache. Cannot save.",
                    table_name
                )
            })?;

        let data_map: HashMap<String, String> = fields
            .iter()
            .zip(values.iter())
            .map(|(field_def, value)| (field_def.data_key.clone(), value.clone()))
            .collect();

        let converted_data =
            data_converter::convert_and_validate_data(&data_map, schema)
            .map_err(|user_error| anyhow!(user_error))?;

        let is_new_entry = id == 0
            || (total_count > 0 && current_position > total_count)
            || (total_count == 0 && current_position == 1);

        let outcome = if is_new_entry {
            let response = grpc_client
                .post_table_data(profile_name.clone(), table_name.clone(), converted_data)
                .await
                .context("Failed to post new table data")?;

            if response.success {
                if let Some(fs) = app_state.form_state_for_path(path) {
                    fs.id = response.inserted_id;
                    fs.total_count += 1;
                    fs.current_position = fs.total_count;
                    fs.has_unsaved_changes = false;
                }
                SaveOutcome::CreatedNew(response.inserted_id)
            } else {
                return Err(anyhow!("Server failed to insert data: {}", response.message));
            }
        } else {
            if id == 0 {
                return Err(anyhow!(
                    "Cannot update record: ID is 0, but not classified as new entry."
                ));
            }
            let response = grpc_client
                .put_table_data(profile_name.clone(), table_name.clone(), id, converted_data)
                .await
                .context("Failed to put (update) table data")?;

            if response.success {
                if let Some(fs) = app_state.form_state_for_path(path) {
                    fs.has_unsaved_changes = false;
                }
                SaveOutcome::UpdatedExisting
            } else {
                return Err(anyhow!("Server failed to update data: {}", response.message));
            }
        };

        Ok(outcome)
    } else {
        Ok(SaveOutcome::NoChange)
    }
}

pub async fn revert(
    app_state: &mut AppState,
    path: &str,
    grpc_client: &mut GrpcClient,
) -> Result<String> {
    if let Some(fs) = app_state.form_state_for_path(path) {
        if fs.id == 0
            || (fs.total_count > 0 && fs.current_position > fs.total_count)
            || (fs.total_count == 0 && fs.current_position == 1)
        {
            let old_total_count = fs.total_count;
            fs.reset_to_empty();
            fs.total_count = old_total_count;
            if fs.total_count > 0 {
                fs.current_position = fs.total_count + 1;
            } else {
                fs.current_position = 1;
            }
            return Ok("New entry cleared".to_string());
        }

        if fs.current_position == 0 || fs.current_position > fs.total_count {
            if fs.total_count > 0 {
                fs.current_position = 1;
            } else {
                fs.reset_to_empty();
                return Ok("No saved data to revert to; form cleared.".to_string());
            }
        }

        let response = grpc_client
            .get_table_data_by_position(
                fs.profile_name.clone(),
                fs.table_name.clone(),
                fs.current_position as i32,
            )
            .await
            .context(format!(
                "Failed to get table data by position {} for table {}.{}",
                fs.current_position, fs.profile_name, fs.table_name
            ))?;

        fs.update_from_response(&response.data, fs.current_position);
        Ok("Changes discarded, reloaded last saved version".to_string())
    } else {
        Ok("Nothing to revert".to_string())
    }
}

pub async fn handle_action(
    action: &str,
    form_state: &mut FormState,
    _grpc_client: &mut GrpcClient,
    ideal_cursor_column: &mut usize,
) -> Result<String> {
    if form_state.has_unsaved_changes() {
        return Ok(
            "Unsaved changes. Save (Ctrl+S) or Revert (Ctrl+R) before navigating."
                .to_string(),
        );
    }

    let total_count = form_state.total_count;

    match action {
        "previous_entry" => {
            if form_state.current_position > 1 {
                form_state.current_position -= 1;
                *ideal_cursor_column = 0;
            }
        }
        "next_entry" => {
            if form_state.current_position <= total_count {
                form_state.current_position += 1;
                *ideal_cursor_column = 0;
            }
        }
        _ => return Err(anyhow!("Unknown form action: {}", action)),
    }

    Ok(String::new())
}
