use crate::agent::{
    AgentRuntimeConfig, AgentStoredSettings, AgentTaskRoute, ProviderMode, TaskProfile,
};
use crate::error::AppError;

pub fn resolve_runtime(
    settings: &AgentStoredSettings,
    task_profile: TaskProfile,
    provider_override: Option<ProviderMode>,
    model_override: Option<String>,
) -> Result<AgentRuntimeConfig, AppError> {
    let route = settings
        .task_defaults
        .get(&task_profile)
        .ok_or(AppError::TaskRouteMissing(task_profile))?;

    let provider_mode = provider_override.unwrap_or(route.provider_mode);
    let model = resolve_model(settings, task_profile, route, provider_mode, model_override)?;

    match provider_mode {
        ProviderMode::Remote => {
            if !settings.remote.enabled {
                return Err(AppError::ProviderNotConfigured(ProviderMode::Remote));
            }

            let api_key = settings.remote.api_key.trim().to_string();
            if api_key.is_empty() {
                return Err(AppError::RemoteApiKeyMissing);
            }

            Ok(AgentRuntimeConfig {
                provider_mode,
                base_url: settings.remote.base_url.clone(),
                model,
                api_key: Some(api_key),
                task_profile,
            })
        }
        ProviderMode::Local => {
            if !settings.local.enabled {
                return Err(AppError::ProviderNotConfigured(ProviderMode::Local));
            }

            Ok(AgentRuntimeConfig {
                provider_mode,
                base_url: settings.local.base_url.clone(),
                model,
                api_key: None,
                task_profile,
            })
        }
    }
}

pub fn validate_settings(settings: &AgentStoredSettings) -> Result<(), AppError> {
    if settings.remote.base_url.trim().is_empty() {
        return Err(AppError::InvalidSettings(
            "remote base URL cannot be empty".to_string(),
        ));
    }

    if settings.local.base_url.trim().is_empty() {
        return Err(AppError::InvalidSettings(
            "local base URL cannot be empty".to_string(),
        ));
    }

    if settings.default_remote_model.trim().is_empty() {
        return Err(AppError::InvalidSettings(
            "default remote model cannot be empty".to_string(),
        ));
    }

    for task in TaskProfile::all() {
        let route = settings
            .task_defaults
            .get(&task)
            .ok_or(AppError::TaskRouteMissing(task))?;
        let model = normalize_route_model(settings, task, route.clone())?.model;

        match route.provider_mode {
            ProviderMode::Remote => {
                if !settings.remote.enabled {
                    return Err(AppError::ProviderNotConfigured(ProviderMode::Remote));
                }
                if model.trim().is_empty() {
                    return Err(AppError::ModelMissing(task));
                }
            }
            ProviderMode::Local => {
                if !settings.local.enabled {
                    return Err(AppError::ProviderNotConfigured(ProviderMode::Local));
                }
                if model.trim().is_empty() {
                    return Err(AppError::ModelMissing(task));
                }
            }
        }
    }

    Ok(())
}

pub fn normalize_routes(settings: &mut AgentStoredSettings) -> Result<(), AppError> {
    for task in TaskProfile::all() {
        let route = settings
            .task_defaults
            .get(&task)
            .cloned()
            .ok_or(AppError::TaskRouteMissing(task))?;
        let normalized = normalize_route_model(settings, task, route)?;
        settings.task_defaults.insert(task, normalized);
    }

    Ok(())
}

pub fn compute_remote_configured(settings: &AgentStoredSettings) -> bool {
    settings.remote.enabled
        && !settings.remote.base_url.trim().is_empty()
        && !settings.remote.api_key.trim().is_empty()
        && !settings.default_remote_model.trim().is_empty()
}

pub fn compute_local_configured(settings: &AgentStoredSettings) -> bool {
    settings.local.enabled && !settings.local.base_url.trim().is_empty()
}

pub fn compute_overall_configured(settings: &AgentStoredSettings) -> bool {
    if validate_settings(settings).is_err() {
        return false;
    }

    let route = match settings.task_defaults.get(&TaskProfile::InteractiveChat) {
        Some(route) => route,
        None => return false,
    };

    match route.provider_mode {
        ProviderMode::Remote => compute_remote_configured(settings),
        ProviderMode::Local => compute_local_configured(settings),
    }
}

fn resolve_model(
    settings: &AgentStoredSettings,
    task_profile: TaskProfile,
    route: &AgentTaskRoute,
    provider_mode: ProviderMode,
    model_override: Option<String>,
) -> Result<String, AppError> {
    if let Some(model) = model_override {
        let trimmed = model.trim();
        if trimmed.is_empty() {
            return Err(AppError::ModelMissing(task_profile));
        }
        return Ok(trimmed.to_string());
    }

    let normalized = normalize_route_model(settings, task_profile, route.clone())?;

    match provider_mode {
        ProviderMode::Remote if normalized.provider_mode == ProviderMode::Local => {
            Ok(settings.default_remote_model.clone())
        }
        ProviderMode::Local if normalized.provider_mode == ProviderMode::Remote => {
            let fallback = settings
                .local
                .available_models
                .first()
                .cloned()
                .ok_or(AppError::ModelMissing(task_profile))?;
            Ok(fallback)
        }
        _ => Ok(normalized.model),
    }
}

fn normalize_route_model(
    settings: &AgentStoredSettings,
    task_profile: TaskProfile,
    route: AgentTaskRoute,
) -> Result<AgentTaskRoute, AppError> {
    let trimmed = route.model.trim();

    match route.provider_mode {
        ProviderMode::Remote => Ok(AgentTaskRoute {
            provider_mode: ProviderMode::Remote,
            model: if trimmed.is_empty() {
                settings.default_remote_model.clone()
            } else {
                trimmed.to_string()
            },
        }),
        ProviderMode::Local => {
            if !trimmed.is_empty() {
                return Ok(AgentTaskRoute {
                    provider_mode: ProviderMode::Local,
                    model: trimmed.to_string(),
                });
            }

            if let Some(model) = settings.local.available_models.first() {
                return Ok(AgentTaskRoute {
                    provider_mode: ProviderMode::Local,
                    model: model.clone(),
                });
            }

            Err(AppError::ModelMissing(task_profile))
        }
    }
}
