use crate::core::shared::models::UserSession;
use crate::core::shared::state::AppState;
use chrono::Utc;
use diesel::prelude::*;
use log::{error, trace};
use rhai::{Dynamic, Engine};
use serde_json::json;
use std::sync::Arc;
use uuid::Uuid;

pub fn add_member_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
    let state_clone = Arc::clone(&state);
    let user_clone = user.clone();

    engine
        .register_custom_syntax(
            ["ADD_MEMBER", "$expr$", ",", "$expr$", ",", "$expr$"],
            false,
            move |context, inputs| {
                let group_id = context.eval_expression_tree(&inputs[0])?.to_string();
                let user_email = context.eval_expression_tree(&inputs[1])?.to_string();
                let role = context.eval_expression_tree(&inputs[2])?.to_string();

                trace!(
                    "ADD_MEMBER: group={}, user_email={}, role={} for user={}",
                    group_id,
                    user_email,
                    role,
                    user_clone.user_id
                );

                let state_for_task = Arc::clone(&state_clone);
                let user_for_task = user_clone.clone();

                let (tx, rx) = std::sync::mpsc::channel();

                std::thread::spawn(move || {
                    let rt = tokio::runtime::Builder::new_multi_thread()
                        .worker_threads(2)
                        .enable_all()
                        .build();

                    let send_err = if let Ok(_rt) = rt {
                        let result = execute_add_member(
                            &state_for_task,
                            &user_for_task,
                            &group_id,
                            &user_email,
                            &role,
                        );
                        tx.send(result).err()
                    } else {
                        tx.send(Err("Failed to build tokio runtime".to_string()))
                            .err()
                    };

                    if send_err.is_some() {
                        error!("Failed to send ADD_MEMBER result from thread");
                    }
                });

                match rx.recv_timeout(std::time::Duration::from_secs(10)) {
                    Ok(Ok(member_id)) => Ok(Dynamic::from(member_id)),
                    Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
                        format!("ADD_MEMBER failed: {}", e).into(),
                        rhai::Position::NONE,
                    ))),
                    Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
                        Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
                            "ADD_MEMBER timed out".into(),
                            rhai::Position::NONE,
                        )))
                    }
                    Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
                        format!("ADD_MEMBER thread failed: {}", e).into(),
                        rhai::Position::NONE,
                    ))),
                }
            },
        )
        .expect("valid syntax registration");

    let state_clone2 = Arc::clone(&state);
    let user_clone2 = user;

    engine
        .register_custom_syntax(
            ["CREATE_TEAM", "$expr$", ",", "$expr$", ",", "$expr$"],
            false,
            move |context, inputs| {
                let name = context.eval_expression_tree(&inputs[0])?.to_string();
                let members_input = context.eval_expression_tree(&inputs[1])?;
                let workspace_template = context.eval_expression_tree(&inputs[2])?.to_string();

                let mut members = Vec::new();
                if members_input.is_array() {
                    let arr = members_input.cast::<rhai::Array>();
                    for item in arr.iter() {
                        members.push(item.to_string());
                    }
                } else {
                    members.push(members_input.to_string());
                }

                trace!(
                    "CREATE_TEAM: name={}, members={:?}, template={} for user={}",
                    name,
                    members,
                    workspace_template,
                    user_clone2.user_id
                );

                let state_for_task = Arc::clone(&state_clone2);
                let user_for_task = user_clone2.clone();

                let (tx, rx) = std::sync::mpsc::channel();

                std::thread::spawn(move || {
                    let rt = tokio::runtime::Builder::new_multi_thread()
                        .worker_threads(2)
                        .enable_all()
                        .build();

                    let send_err = if let Ok(_rt) = rt {
                        let result = execute_create_team(
                            &state_for_task,
                            &user_for_task,
                            &name,
                            members,
                            &workspace_template,
                        );
                        tx.send(result).err()
                    } else {
                        tx.send(Err("Failed to build tokio runtime".to_string()))
                            .err()
                    };

                    if send_err.is_some() {
                        error!("Failed to send CREATE_TEAM result from thread");
                    }
                });

                match rx.recv_timeout(std::time::Duration::from_secs(15)) {
                    Ok(Ok(team_id)) => Ok(Dynamic::from(team_id)),
                    Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
                        format!("CREATE_TEAM failed: {}", e).into(),
                        rhai::Position::NONE,
                    ))),
                    Err(_) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
                        "CREATE_TEAM timed out".into(),
                        rhai::Position::NONE,
                    ))),
                }
            },
        )
        .expect("valid syntax registration");
}

pub fn execute_add_member(
    state: &AppState,
    user: &UserSession,
    group_id: &str,
    user_email: &str,
    role: &str,
) -> Result<String, String> {
    let member_id = Uuid::new_v4().to_string();

    let valid_role = validate_role(role);

    let permissions = get_permissions_for_role(&valid_role);

    let mut conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?;

    let query = diesel::sql_query(
        "INSERT INTO group_members (id, group_id, user_email, role, permissions, added_by, added_at, is_active)
         VALUES ($1, $2, $3, $4, $5, $6, $7, true)
         ON CONFLICT (group_id, user_email)
         DO UPDATE SET role = $4, permissions = $5, updated_at = $7"
    )
    .bind::<diesel::sql_types::Text, _>(&member_id)
    .bind::<diesel::sql_types::Text, _>(group_id)
    .bind::<diesel::sql_types::Text, _>(user_email)
    .bind::<diesel::sql_types::Text, _>(&valid_role)
    .bind::<diesel::sql_types::Jsonb, _>(&permissions);

    let user_id_str = user.user_id.to_string();
    let now = Utc::now();
    let query = query
        .bind::<diesel::sql_types::Text, _>(&user_id_str)
        .bind::<diesel::sql_types::Timestamptz, _>(&now);

    query.execute(&mut *conn).map_err(|e| {
        error!("Failed to add member: {}", e);
        format!("Failed to add member: {}", e)
    })?;

    send_member_invitation(state, group_id, user_email, &valid_role)?;

    log_group_activity(state, group_id, "member_added", user_email)?;

    trace!(
        "Added {} to group {} as {} with permissions {:?}",
        user_email,
        group_id,
        valid_role,
        permissions
    );

    Ok(member_id)
}

fn execute_create_team(
    state: &AppState,
    user: &UserSession,
    name: &str,
    members: Vec<String>,
    workspace_template: &str,
) -> Result<String, String> {
    let team_id = Uuid::new_v4().to_string();

    let mut conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?;

    let query = diesel::sql_query(
        "INSERT INTO groups (id, name, type, template, created_by, created_at, settings)
         VALUES ($1, $2, $3, $4, $5, $6, $7)",
    )
    .bind::<diesel::sql_types::Text, _>(&team_id)
    .bind::<diesel::sql_types::Text, _>(name)
    .bind::<diesel::sql_types::Text, _>("team")
    .bind::<diesel::sql_types::Text, _>(workspace_template);

    let user_id_str = user.user_id.to_string();
    let now = Utc::now();
    let permissions_json = serde_json::to_value(json!({
        "workspace_enabled": true,
        "chat_enabled": true,
        "file_sharing": true
    }))
    .expect("valid syntax registration");

    let query = query
        .bind::<diesel::sql_types::Text, _>(&user_id_str)
        .bind::<diesel::sql_types::Timestamptz, _>(&now)
        .bind::<diesel::sql_types::Jsonb, _>(&permissions_json);

    query.execute(&mut *conn).map_err(|e| {
        error!("Failed to create team: {}", e);
        format!("Failed to create team: {}", e)
    })?;

    execute_add_member(state, user, &team_id, &user.user_id.to_string(), "admin")?;

    for member_email in &members {
        let role = if member_email == &user.user_id.to_string() {
            "admin"
        } else {
            "member"
        };
        execute_add_member(state, user, &team_id, member_email, role)?;
    }

    create_workspace_structure(state, &team_id, name, workspace_template)?;

    create_team_channel(state, &team_id, name)?;

    trace!(
        "Created team '{}' with {} members (ID: {})",
        name,
        members.len(),
        team_id
    );

    Ok(team_id)
}

fn validate_role(role: &str) -> String {
    match role.to_lowercase().as_str() {
        "admin" | "administrator" => "admin",
        "contributor" | "editor" => "contributor",
        "viewer" | "read" | "readonly" => "viewer",
        "owner" => "owner",
        _ => "member",
    }
    .to_string()
}

fn get_permissions_for_role(role: &str) -> serde_json::Value {
    match role {
        "owner" | "admin" => json!({
            "read": true,
            "write": true,
            "delete": true,
            "manage_members": true,
            "manage_settings": true,
            "export_data": true
        }),
        "contributor" => json!({
            "read": true,
            "write": true,
            "delete": false,
            "manage_members": false,
            "manage_settings": false,
            "export_data": true
        }),
        "member" => json!({
            "read": true,
            "write": true,
            "delete": false,
            "manage_members": false,
            "manage_settings": false,
            "export_data": false
        }),
        _ => json!({
            "read": true,
            "write": false,
            "delete": false,
            "manage_members": false,
            "manage_settings": false,
            "export_data": false
        }),
    }
}

fn send_member_invitation(
    _state: &AppState,
    group_id: &str,
    user_email: &str,
    role: &str,
) -> Result<(), String> {
    trace!(
        "Invitation sent to {} for group {} with role {}",
        user_email,
        group_id,
        role
    );
    Ok(())
}

fn log_group_activity(
    state: &AppState,
    group_id: &str,
    action: &str,
    details: &str,
) -> Result<(), String> {
    let mut conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?;

    let activity_id = Uuid::new_v4().to_string();

    let query = diesel::sql_query(
        "INSERT INTO group_activity_log (id, group_id, action, details, timestamp)
         VALUES ($1, $2, $3, $4, $5)",
    )
    .bind::<diesel::sql_types::Text, _>(&activity_id)
    .bind::<diesel::sql_types::Text, _>(group_id)
    .bind::<diesel::sql_types::Text, _>(action)
    .bind::<diesel::sql_types::Text, _>(details);

    let now = Utc::now();
    let query = query.bind::<diesel::sql_types::Timestamptz, _>(&now);

    query.execute(&mut *conn).map_err(|e| {
        error!("Failed to log activity: {}", e);
        format!("Failed to log activity: {}", e)
    })?;

    Ok(())
}

fn create_workspace_structure(
    state: &AppState,
    team_id: &str,
    team_name: &str,
    template: &str,
) -> Result<(), String> {
    let mut conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?;

    let folders = match template {
        "project" => vec![
            "Documents",
            "Meetings",
            "Resources",
            "Deliverables",
            "Archive",
        ],
        "sales" => vec!["Proposals", "Contracts", "Presentations", "CRM", "Reports"],
        "support" => vec![
            "Tickets",
            "Knowledge Base",
            "FAQs",
            "Training",
            "Escalations",
        ],
        _ => vec!["Documents", "Shared", "Archive"],
    };

    let workspace_base = format!(".gbdrive/workspaces/{}", team_name);

    for folder in folders {
        let folder_path = format!("{}/{}", workspace_base, folder);

        let folder_id = Uuid::new_v4().to_string();
        let query = diesel::sql_query(
            "INSERT INTO workspace_folders (id, team_id, path, name, created_at)
             VALUES ($1, $2, $3, $4, $5)",
        )
        .bind::<diesel::sql_types::Text, _>(&folder_id);
        let now = chrono::Utc::now();
        let query = query
            .bind::<diesel::sql_types::Text, _>(team_id)
            .bind::<diesel::sql_types::Text, _>(&folder_path)
            .bind::<diesel::sql_types::Text, _>(folder)
            .bind::<diesel::sql_types::Timestamptz, _>(&now);

        query.execute(&mut *conn).map_err(|e| {
            error!("Failed to create workspace folder: {}", e);
            format!("Failed to create workspace folder: {}", e)
        })?;
    }

    trace!("Created workspace structure for team {}", team_name);
    Ok(())
}

fn create_team_channel(state: &AppState, team_id: &str, team_name: &str) -> Result<(), String> {
    let mut conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?;

    let channel_id = Uuid::new_v4().to_string();
    let now = Utc::now();

    let query = diesel::sql_query(
        "INSERT INTO communication_channels (id, team_id, name, type, created_at)
         VALUES ($1, $2, $3, 'team_chat', $4)",
    )
    .bind::<diesel::sql_types::Text, _>(&channel_id)
    .bind::<diesel::sql_types::Text, _>(team_id)
    .bind::<diesel::sql_types::Text, _>(team_name)
    .bind::<diesel::sql_types::Timestamptz, _>(&now);

    query.execute(&mut *conn).map_err(|e| {
        error!("Failed to create team channel: {}", e);
        format!("Failed to create team channel: {}", e)
    })?;

    trace!("Created communication channel for team {}", team_name);
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validate_role() {
        assert_eq!(validate_role("admin"), "admin");
        assert_eq!(validate_role("ADMIN"), "admin");
        assert_eq!(validate_role("contributor"), "contributor");
        assert_eq!(validate_role("viewer"), "viewer");
        assert_eq!(validate_role("unknown"), "member");
    }

    #[test]
    fn test_get_permissions_for_role() {
        let admin_perms = get_permissions_for_role("admin");
        assert!(admin_perms.get("read").unwrap().as_bool().unwrap());
        assert!(admin_perms.get("write").unwrap().as_bool().unwrap());
        assert!(admin_perms
            .get("manage_members")
            .unwrap()
            .as_bool()
            .unwrap());

        let viewer_perms = get_permissions_for_role("viewer");
        assert!(viewer_perms.get("read").unwrap().as_bool().unwrap());
        assert!(!viewer_perms.get("write").unwrap().as_bool().unwrap());
        assert!(!viewer_perms.get("delete").unwrap().as_bool().unwrap());
    }
}
