use crate::error::AppError;
use crate::models::agent::{Agent, AgentRole};
use crate::models::credential::TuleapCredentials;
use crate::models::ticket::ProcessedTicket;
use crate::models::tracker::{FilterGroup, TrackerUpdate, WatchedTracker};
use crate::services::crypto;
use crate::services::tuleap_client::TuleapClient;
use crate::AppState;
use tauri::State;

fn build_tuleap_client(state: &State<AppState>) -> Result<TuleapClient, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let cred = TuleapCredentials::get(&db)?
        .ok_or_else(|| AppError::from("No Tuleap credentials configured".to_string()))?;

    let password = crypto::decrypt(&state.encryption_key, &cred.password_encrypted)
        .map_err(AppError::from)?;

    Ok(TuleapClient::new(
        &state.http_client,
        &cred.tuleap_url,
        &cred.username,
        &password,
    ))
}

fn ensure_agent_role(db: &rusqlite::Connection, agent_id: &str, expected: AgentRole) -> Result<(), AppError> {
    let agent = Agent::get_by_id(db, agent_id)?;
    if agent.role != expected {
        return Err(AppError::from(format!(
            "Agent '{}' has role '{}', expected '{}'",
            agent.name,
            agent.role.as_str(),
            expected.as_str()
        )));
    }
    Ok(())
}

#[tauri::command]
pub fn add_tracker(
    state: State<'_, AppState>,
    project_id: String,
    tracker_id: i32,
    tracker_label: String,
    polling_interval: i32,
    analyst_agent_id: String,
    developer_agent_id: String,
    filters: Vec<FilterGroup>,
) -> Result<WatchedTracker, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    ensure_agent_role(&db, &analyst_agent_id, AgentRole::Analyst)?;
    ensure_agent_role(&db, &developer_agent_id, AgentRole::Developer)?;

    let tracker = WatchedTracker::insert(
        &db,
        &project_id,
        tracker_id,
        &tracker_label,
        polling_interval,
        &analyst_agent_id,
        &developer_agent_id,
        filters,
    )?;

    Ok(tracker)
}

#[tauri::command]
pub fn list_trackers(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<Vec<WatchedTracker>, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let trackers = WatchedTracker::list_by_project(&db, &project_id)?;
    Ok(trackers)
}

#[tauri::command]
pub fn update_tracker(
    state: State<'_, AppState>,
    id: String,
    update: TrackerUpdate,
) -> Result<(), AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    ensure_agent_role(&db, &update.analyst_agent_id, AgentRole::Analyst)?;
    ensure_agent_role(&db, &update.developer_agent_id, AgentRole::Developer)?;

    WatchedTracker::update(&db, &id, update)?;
    Ok(())
}

#[tauri::command]
pub fn remove_tracker(state: State<'_, AppState>, id: String) -> Result<(), AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    WatchedTracker::delete(&db, &id)?;
    Ok(())
}

#[tauri::command]
pub async fn get_tracker_fields(
    state: State<'_, AppState>,
    tracker_id: i32,
) -> Result<Vec<crate::services::tuleap_client::TrackerField>, AppError> {
    let client = build_tuleap_client(&state)?;
    let mut fields = client
        .get_tracker_fields(tracker_id)
        .await
        .map_err(AppError::from)?;

    fields.sort_by(|a, b| a.label.to_lowercase().cmp(&b.label.to_lowercase()));
    for field in &mut fields {
        field
            .values
            .sort_by(|a, b| a.label.to_lowercase().cmp(&b.label.to_lowercase()));
    }

    Ok(fields)
}

#[tauri::command]
pub fn list_processed_tickets(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<Vec<ProcessedTicket>, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let tickets = ProcessedTicket::list_by_project(&db, &project_id)?;
    Ok(tickets)
}
