use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use tauri::State;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Project {
    pub id: i64,
    pub name: String,
    pub key: Option<String>,
    pub description: Option<String>,
    pub status: String,
    pub start_date: Option<String>,
    pub end_date: Option<String>,
    pub hus_count: Option<i64>,
    pub epics_count: Option<i64>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkItem {
    pub id: i64,
    pub project_id: i64,
    pub code: Option<String>,
    pub title: String,
    pub description: Option<String>,
    #[serde(rename = "type")]
    pub wi_type: String,
    pub status: String,
    pub priority: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AlphaUser {
    pub id: i64,
    pub email: String,
    pub first_name: String,
    pub last_name: String,
    pub role: Option<String>,
    pub seniority: Option<String>,
    pub cell_id: Option<i64>,
    pub is_active: bool,
    pub created_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Cell {
    pub id: i64,
    pub name: String,
    pub description: Option<String>,
    pub created_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(dead_code)]
pub struct CellMember {
    pub id: i64,
    pub cell_id: i64,
    pub user_id: i64,
    pub joined_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectMember {
    pub id: i64,
    pub user_id: i64,
    pub initiative_id: i64,
    pub initiative_name: Option<String>,
    pub role: Option<String>,
    pub assigned_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Absence {
    pub id: i64,
    pub user_id: i64,
    pub start_date: String,
    pub end_date: String,
    #[serde(rename = "type")]
    pub absence_type: String,
    pub reason: Option<String>,
    pub created_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DailyLog {
    pub id: i64,
    pub user_id: i64,
    pub initiative_id: i64,
    pub date: String,
    pub work_item_id: Option<i64>,
    pub what_worked_on: Option<String>,
    pub progress_pct: Option<f64>,
    pub impediments: Option<String>,
    pub plan_for_tomorrow: Option<String>,
    pub hours_spent: Option<f64>,
    pub created_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PerformanceSnapshot {
    pub id: i64,
    pub user_id: i64,
    pub initiative_id: Option<i64>,
    pub period: String,
    pub planned_sp: Option<f64>,
    pub completed_sp: Option<f64>,
    pub planned_hours: Option<f64>,
    pub actual_hours: Option<f64>,
    pub velocity: Option<f64>,
    pub spi: Option<f64>,
    pub cpi: Option<f64>,
    pub compliance_pct: Option<f64>,
    pub impediment_count: Option<i64>,
    pub calculated_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Epic {
    pub id: i64,
    pub initiative_id: i64,
    pub code: Option<String>,
    pub name: String,
    pub description: Option<String>,
    pub status: Option<String>,
    pub client_taker: Option<i64>,
    pub stimated_start_date: Option<String>,
    pub stimated_end_date: Option<String>,
    pub start_date: Option<String>,
    pub end_date: Option<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
    pub item_type: Option<String>,
    pub hierarchy_path: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserStory {
    pub id: i64,
    pub initiative_id: i64,
    pub epic_id: Option<i64>,
    pub code: Option<String>,
    pub title: String,
    pub description: Option<String>,
    pub acceptance_criteria: Option<String>,
    pub status: Option<String>,
    pub priority: Option<String>,
    pub story_points: Option<f64>,
    pub estimated_hours: Option<f64>,
    pub actual_hours: Option<f64>,
    pub assigned_to: Option<i64>,
    pub sprint: Option<String>,
    pub has_impairment: bool,
    pub created_at: Option<String>,
    pub item_type: Option<String>,
    pub hierarchy_path: Option<String>,
    pub parent_code: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Impairment {
    pub id: i64,
    pub hu_id: i64,
    pub responsible: Option<String>,
    pub pending_activity: Option<String>,
    pub pending_type: Option<String>,
    pub type_impediment: bool,
    pub delivery_date: Option<String>,
    pub status: bool,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HuDraft {
    pub id: String,
    pub initiative_id: Option<i64>,
    pub code: Option<String>,
    pub title: String,
    pub description: Option<String>,
    pub acceptance_criteria: Option<String>,
    pub item_type: Option<String>,
    pub hierarchy_path: Option<String>,
    pub story_points: Option<f64>,
    pub sprint: Option<String>,
    pub assigned_to: Option<String>,
    pub sync_status: String,
    pub kappa_id: Option<i64>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

async fn get_conn(db_path: &str) -> Result<libsql::Connection, String> {
    let db = libsql::Builder::new_local(db_path)
        .build()
        .await
        .map_err(|e| format!("DB open: {e}"))?;

    let conn = db.connect().map_err(|e| format!("Connect: {e}"))?;

    conn.execute_batch(
        "        CREATE TABLE IF NOT EXISTS projects (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            key TEXT,
            description TEXT,
            status TEXT DEFAULT 'active',
            start_date TEXT,
            end_date TEXT,
            hus_count INTEGER DEFAULT 0,
            epics_count INTEGER DEFAULT 0,
            created_at TEXT DEFAULT (datetime('now'))
        );

        CREATE TABLE IF NOT EXISTS work_items (
            id INTEGER PRIMARY KEY,
            project_id INTEGER NOT NULL,
            code TEXT,
            title TEXT NOT NULL,
            description TEXT,
            type TEXT DEFAULT 'task',
            status TEXT DEFAULT 'backlog',
            priority TEXT DEFAULT 'medium',
            created_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (project_id) REFERENCES projects(id)
        );

        CREATE TABLE IF NOT EXISTS alpha_users (
            id INTEGER PRIMARY KEY,
            email TEXT NOT NULL,
            first_name TEXT DEFAULT '',
            last_name TEXT DEFAULT '',
            role TEXT,
            seniority TEXT,
            cell_id INTEGER,
            is_active INTEGER DEFAULT 1,
            created_at TEXT DEFAULT (datetime('now'))
        );

        CREATE TABLE IF NOT EXISTS cells (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            description TEXT,
            created_at TEXT DEFAULT (datetime('now'))
        );

        CREATE TABLE IF NOT EXISTS cell_members (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            cell_id INTEGER NOT NULL,
            user_id INTEGER NOT NULL,
            joined_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (cell_id) REFERENCES cells(id),
            FOREIGN KEY (user_id) REFERENCES alpha_users(id)
        );

        CREATE TABLE IF NOT EXISTS project_members (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            initiative_id INTEGER NOT NULL,
            initiative_name TEXT,
            role TEXT,
            assigned_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (user_id) REFERENCES alpha_users(id)
        );

        CREATE TABLE IF NOT EXISTS absences (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            start_date TEXT NOT NULL,
            end_date TEXT NOT NULL,
            type TEXT DEFAULT 'vacation',
            reason TEXT,
            created_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (user_id) REFERENCES alpha_users(id)
        );

        CREATE TABLE IF NOT EXISTS daily_logs (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            initiative_id INTEGER NOT NULL,
            date TEXT NOT NULL,
            work_item_id INTEGER,
            what_worked_on TEXT,
            progress_pct REAL,
            impediments TEXT,
            plan_for_tomorrow TEXT,
            hours_spent REAL,
            created_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (user_id) REFERENCES alpha_users(id),
            FOREIGN KEY (work_item_id) REFERENCES work_items(id)
        );

        CREATE TABLE IF NOT EXISTS performance_snapshots (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            initiative_id INTEGER,
            period TEXT NOT NULL DEFAULT 'weekly',
            planned_sp REAL,
            completed_sp REAL,
            planned_hours REAL,
            actual_hours REAL,
            velocity REAL,
            spi REAL,
            cpi REAL,
            compliance_pct REAL,
            impediment_count INTEGER,
            calculated_at TEXT DEFAULT (datetime('now')),
            FOREIGN KEY (user_id) REFERENCES alpha_users(id)
        );

        CREATE TABLE IF NOT EXISTS impairments (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            hu_id INTEGER NOT NULL,
            responsible TEXT,
            pending_activity TEXT,
            pending_type TEXT,
            type_impediment INTEGER DEFAULT 0,
            delivery_date TEXT,
            status INTEGER DEFAULT 0,
            created_at TEXT,
            updated_at TEXT,
            FOREIGN KEY (hu_id) REFERENCES user_stories(id)
        );

        CREATE TABLE IF NOT EXISTS hu_drafts (
            id TEXT PRIMARY KEY,
            initiative_id INTEGER,
            code TEXT,
            title TEXT NOT NULL,
            description TEXT,
            acceptance_criteria TEXT,
            item_type TEXT DEFAULT 'U',
            hierarchy_path TEXT,
            story_points REAL,
            sprint TEXT,
            assigned_to TEXT,
            sync_status TEXT DEFAULT 'draft',
            kappa_id INTEGER,
            created_at TEXT DEFAULT (datetime('now')),
            updated_at TEXT DEFAULT (datetime('now'))
        );"
    )
    .await
    .map_err(|e| format!("Migration: {e}"))?;

    // Migraciones post-CREATE: agregar columnas que pueden no existir en BD viejas
    for alter in &[
        "ALTER TABLE projects ADD COLUMN hus_count INTEGER DEFAULT 0",
        "ALTER TABLE projects ADD COLUMN epics_count INTEGER DEFAULT 0",
        "ALTER TABLE epics ADD COLUMN item_type TEXT DEFAULT 'E'",
        "ALTER TABLE epics ADD COLUMN hierarchy_path TEXT",
        "ALTER TABLE user_stories ADD COLUMN item_type TEXT DEFAULT 'U'",
        "ALTER TABLE user_stories ADD COLUMN hierarchy_path TEXT",
        "ALTER TABLE user_stories ADD COLUMN parent_code TEXT",
        "ALTER TABLE work_items ADD COLUMN epic_id INTEGER",
        "ALTER TABLE work_items ADD COLUMN parent_type TEXT",
        "ALTER TABLE work_items ADD COLUMN parent_id INTEGER",
        "ALTER TABLE work_items ADD COLUMN story_points REAL",
        "ALTER TABLE work_items ADD COLUMN estimated_hours REAL",
        "ALTER TABLE work_items ADD COLUMN actual_hours REAL",
        "ALTER TABLE work_items ADD COLUMN assigned_to INTEGER",
        "ALTER TABLE user_stories ADD COLUMN sprint TEXT",
        "ALTER TABLE user_stories ADD COLUMN has_impairment INTEGER DEFAULT 0",
    ] {
        let _ = conn.execute(alter, ()).await;
    }

    Ok(conn)
}

pub mod commands {
    use super::*;

    // ────────────────────────────────────────────
    // Projects
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_projects(state: State<'_, Mutex<String>>) -> Result<Vec<Project>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, name, key, description, status, start_date, end_date, hus_count, epics_count FROM projects ORDER BY name", ())
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut projects = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            projects.push(Project {
                id: row.get::<i64>(0).unwrap_or(0),
                name: row.get::<String>(1).unwrap_or_default(),
                key: row.get::<String>(2).ok(),
                description: row.get::<String>(3).ok(),
                status: row.get::<String>(4).unwrap_or_else(|_| "active".into()),
                start_date: row.get::<String>(5).ok(),
                end_date: row.get::<String>(6).ok(),
                hus_count: row.get::<i64>(7).ok(),
                epics_count: row.get::<i64>(8).ok(),
            });
        }
        Ok(projects)
    }

    #[tauri::command]
    pub async fn save_project(state: State<'_, Mutex<String>>, project: Project) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO projects (id, name, key, description, status, start_date, end_date, hus_count, epics_count)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
            libsql::params![
                project.id,
                project.name,
                project.key,
                project.description,
                project.status,
                project.start_date,
                project.end_date,
                project.hus_count,
                project.epics_count,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn update_project_counts(state: State<'_, Mutex<String>>, initiative_id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "UPDATE projects SET
                hus_count = (SELECT COUNT(*) FROM user_stories WHERE initiative_id = ?1),
                epics_count = (SELECT COUNT(*) FROM epics WHERE initiative_id = ?1)
             WHERE id = ?1",
            libsql::params![initiative_id],
        )
        .await
        .map_err(|e| format!("Update counts: {e}"))?;

        Ok(())
    }

    #[tauri::command]
    pub async fn delete_project(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM work_items WHERE project_id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete WIs: {e}"))?;

        conn.execute("DELETE FROM projects WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete project: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // Work Items
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_work_items(state: State<'_, Mutex<String>>, project_id: i64) -> Result<Vec<WorkItem>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query(
                "SELECT id, project_id, code, title, description, type, status, priority FROM work_items WHERE project_id = ?1 ORDER BY created_at DESC",
                libsql::params![project_id],
            )
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut items = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            items.push(WorkItem {
                id: row.get::<i64>(0).unwrap_or(0),
                project_id: row.get::<i64>(1).unwrap_or(0),
                code: row.get::<String>(2).ok(),
                title: row.get::<String>(3).unwrap_or_default(),
                description: row.get::<String>(4).ok(),
                wi_type: row.get::<String>(5).unwrap_or_else(|_| "task".into()),
                status: row.get::<String>(6).unwrap_or_else(|_| "backlog".into()),
                priority: row.get::<String>(7).unwrap_or_else(|_| "medium".into()),
            });
        }
        Ok(items)
    }

    #[tauri::command]
    pub async fn save_work_item(state: State<'_, Mutex<String>>, item: WorkItem) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO work_items (id, project_id, code, title, description, type, status, priority)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
            libsql::params![
                item.id,
                item.project_id,
                item.code,
                item.title,
                item.description,
                item.wi_type,
                item.status,
                item.priority,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn delete_work_item(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM work_items WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // Users (Alpha)
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_users(state: State<'_, Mutex<String>>) -> Result<Vec<AlphaUser>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, email, first_name, last_name, role, seniority, cell_id, is_active, created_at FROM alpha_users ORDER BY first_name", ())
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut users = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            users.push(AlphaUser {
                id: row.get::<i64>(0).unwrap_or(0),
                email: row.get::<String>(1).unwrap_or_default(),
                first_name: row.get::<String>(2).unwrap_or_default(),
                last_name: row.get::<String>(3).unwrap_or_default(),
                role: row.get::<String>(4).ok(),
                seniority: row.get::<String>(5).ok(),
                cell_id: row.get::<i64>(6).ok(),
                is_active: row.get::<i64>(7).unwrap_or(1) != 0,
                created_at: row.get::<String>(8).ok(),
            });
        }
        Ok(users)
    }

    #[tauri::command]
    pub async fn save_user(state: State<'_, Mutex<String>>, user: AlphaUser) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO alpha_users (id, email, first_name, last_name, role, seniority, cell_id, is_active)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
            libsql::params![
                user.id,
                user.email,
                user.first_name,
                user.last_name,
                user.role,
                user.seniority,
                user.cell_id,
                user.is_active as i64,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn update_user_fields(
        state: State<'_, Mutex<String>>,
        id: i64,
        role: Option<String>,
        seniority: Option<String>,
        cell_id: Option<i64>,
    ) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "UPDATE alpha_users SET role = COALESCE(?1, role), seniority = COALESCE(?2, seniority), cell_id = COALESCE(?3, cell_id) WHERE id = ?4",
            libsql::params![role, seniority, cell_id, id],
        )
        .await
        .map_err(|e| format!("Update: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // Cells
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_cells(state: State<'_, Mutex<String>>) -> Result<Vec<Cell>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, name, description, created_at FROM cells ORDER BY name", ())
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut cells = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            cells.push(Cell {
                id: row.get::<i64>(0).unwrap_or(0),
                name: row.get::<String>(1).unwrap_or_default(),
                description: row.get::<String>(2).ok(),
                created_at: row.get::<String>(3).ok(),
            });
        }
        Ok(cells)
    }

    #[tauri::command]
    pub async fn save_cell(state: State<'_, Mutex<String>>, cell: Cell) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        if cell.id != 0 {
            conn.execute(
                "UPDATE cells SET name = ?1, description = ?2 WHERE id = ?3",
                libsql::params![cell.name, cell.description, cell.id],
            ).await.map_err(|e| format!("Update: {e}"))?;
            Ok(cell.id)
        } else {
            conn.execute(
                "INSERT INTO cells (name, description) VALUES (?1, ?2)",
                libsql::params![cell.name, cell.description],
            ).await.map_err(|e| format!("Insert: {e}"))?;
            Ok(conn.last_insert_rowid())
        }
    }

    // ────────────────────────────────────────────
    // Project Members
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn save_project_members(
        state: State<'_, Mutex<String>>,
        user_id: i64,
        members: Vec<ProjectMember>,
    ) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM project_members WHERE user_id = ?1", libsql::params![user_id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        for m in &members {
            conn.execute(
                "INSERT INTO project_members (user_id, initiative_id, initiative_name, role) VALUES (?1, ?2, ?3, ?4)",
                libsql::params![user_id, m.initiative_id, m.initiative_name.clone(), m.role.clone()],
            )
            .await
            .map_err(|e| format!("Insert: {e}"))?;
        }

        Ok(())
    }

    #[tauri::command]
    pub async fn get_project_members(
        state: State<'_, Mutex<String>>,
        user_id: i64,
    ) -> Result<Vec<ProjectMember>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, user_id, initiative_id, initiative_name, role, assigned_at FROM project_members WHERE user_id = ?1 ORDER BY assigned_at DESC", libsql::params![user_id])
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut members = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            members.push(ProjectMember {
                id: row.get::<i64>(0).unwrap_or(0),
                user_id: row.get::<i64>(1).unwrap_or(0),
                initiative_id: row.get::<i64>(2).unwrap_or(0),
                initiative_name: row.get::<String>(3).ok(),
                role: row.get::<String>(4).ok(),
                assigned_at: row.get::<String>(5).ok(),
            });
        }
        Ok(members)
    }

    // ────────────────────────────────────────────
    // Absences
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_absences(state: State<'_, Mutex<String>>, user_id: i64) -> Result<Vec<Absence>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, user_id, start_date, end_date, type, reason, created_at FROM absences WHERE user_id = ?1 ORDER BY start_date DESC", libsql::params![user_id])
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut absences = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            absences.push(Absence {
                id: row.get::<i64>(0).unwrap_or(0),
                user_id: row.get::<i64>(1).unwrap_or(0),
                start_date: row.get::<String>(2).unwrap_or_default(),
                end_date: row.get::<String>(3).unwrap_or_default(),
                absence_type: row.get::<String>(4).unwrap_or_else(|_| "vacation".into()),
                reason: row.get::<String>(5).ok(),
                created_at: row.get::<String>(6).ok(),
            });
        }
        Ok(absences)
    }

    #[tauri::command]
    pub async fn save_absence(state: State<'_, Mutex<String>>, absence: Absence) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        if absence.id != 0 {
            conn.execute(
                "UPDATE absences SET start_date = ?1, end_date = ?2, type = ?3, reason = ?4 WHERE id = ?5",
                libsql::params![absence.start_date, absence.end_date, absence.absence_type, absence.reason, absence.id],
            ).await.map_err(|e| format!("Update: {e}"))?;
            Ok(absence.id)
        } else {
            conn.execute(
                "INSERT INTO absences (user_id, start_date, end_date, type, reason) VALUES (?1, ?2, ?3, ?4, ?5)",
                libsql::params![absence.user_id, absence.start_date, absence.end_date, absence.absence_type, absence.reason],
            ).await.map_err(|e| format!("Insert: {e}"))?;
            Ok(conn.last_insert_rowid())
        }
    }

    #[tauri::command]
    pub async fn delete_absence(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM absences WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // Daily Logs
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_daily_logs(
        state: State<'_, Mutex<String>>,
        user_id: i64,
        initiative_id: Option<i64>,
    ) -> Result<Vec<DailyLog>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let query = if let Some(_pid) = initiative_id {
            "SELECT id, user_id, initiative_id, date, work_item_id, what_worked_on, progress_pct, impediments, plan_for_tomorrow, hours_spent, created_at FROM daily_logs WHERE user_id = ?1 AND initiative_id = ?2 ORDER BY date DESC"
        } else {
            "SELECT id, user_id, initiative_id, date, work_item_id, what_worked_on, progress_pct, impediments, plan_for_tomorrow, hours_spent, created_at FROM daily_logs WHERE user_id = ?1 ORDER BY date DESC"
        };

        let mut rows = if let Some(pid) = initiative_id {
            conn.query(query, libsql::params![user_id, pid]).await.map_err(|e| format!("Query: {e}"))?
        } else {
            conn.query(query, libsql::params![user_id]).await.map_err(|e| format!("Query: {e}"))?
        };

        let mut logs = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            logs.push(DailyLog {
                id: row.get::<i64>(0).unwrap_or(0),
                user_id: row.get::<i64>(1).unwrap_or(0),
                initiative_id: row.get::<i64>(2).unwrap_or(0),
                date: row.get::<String>(3).unwrap_or_default(),
                work_item_id: row.get::<i64>(4).ok(),
                what_worked_on: row.get::<String>(5).ok(),
                progress_pct: row.get::<f64>(6).ok(),
                impediments: row.get::<String>(7).ok(),
                plan_for_tomorrow: row.get::<String>(8).ok(),
                hours_spent: row.get::<f64>(9).ok(),
                created_at: row.get::<String>(10).ok(),
            });
        }
        Ok(logs)
    }

    #[tauri::command]
    pub async fn save_daily_log(state: State<'_, Mutex<String>>, log: DailyLog) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        if log.id != 0 {
            conn.execute(
                "UPDATE daily_logs SET what_worked_on = ?1, progress_pct = ?2, impediments = ?3, plan_for_tomorrow = ?4, hours_spent = ?5 WHERE id = ?6",
                libsql::params![log.what_worked_on, log.progress_pct, log.impediments, log.plan_for_tomorrow, log.hours_spent, log.id],
            ).await.map_err(|e| format!("Update: {e}"))?;
            Ok(log.id)
        } else {
            conn.execute(
                "INSERT INTO daily_logs (user_id, initiative_id, date, work_item_id, what_worked_on, progress_pct, impediments, plan_for_tomorrow, hours_spent) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
                libsql::params![log.user_id, log.initiative_id, log.date, log.work_item_id, log.what_worked_on, log.progress_pct, log.impediments, log.plan_for_tomorrow, log.hours_spent],
            ).await.map_err(|e| format!("Insert: {e}"))?;
            Ok(conn.last_insert_rowid())
        }
    }

    // ────────────────────────────────────────────
    // Performance
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_performance(
        state: State<'_, Mutex<String>>,
        user_id: i64,
        initiative_id: Option<i64>,
    ) -> Result<Vec<PerformanceSnapshot>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let query = if let Some(_pid) = initiative_id {
            "SELECT id, user_id, initiative_id, period, planned_sp, completed_sp, planned_hours, actual_hours, velocity, spi, cpi, compliance_pct, impediment_count, calculated_at FROM performance_snapshots WHERE user_id = ?1 AND initiative_id = ?2 ORDER BY calculated_at DESC"
        } else {
            "SELECT id, user_id, initiative_id, period, planned_sp, completed_sp, planned_hours, actual_hours, velocity, spi, cpi, compliance_pct, impediment_count, calculated_at FROM performance_snapshots WHERE user_id = ?1 ORDER BY calculated_at DESC"
        };

        let mut rows = if let Some(pid) = initiative_id {
            conn.query(query, libsql::params![user_id, pid]).await.map_err(|e| format!("Query: {e}"))?
        } else {
            conn.query(query, libsql::params![user_id]).await.map_err(|e| format!("Query: {e}"))?
        };

        let mut snapshots = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            snapshots.push(PerformanceSnapshot {
                id: row.get::<i64>(0).unwrap_or(0),
                user_id: row.get::<i64>(1).unwrap_or(0),
                initiative_id: row.get::<i64>(2).ok(),
                period: row.get::<String>(3).unwrap_or_default(),
                planned_sp: row.get::<f64>(4).ok(),
                completed_sp: row.get::<f64>(5).ok(),
                planned_hours: row.get::<f64>(6).ok(),
                actual_hours: row.get::<f64>(7).ok(),
                velocity: row.get::<f64>(8).ok(),
                spi: row.get::<f64>(9).ok(),
                cpi: row.get::<f64>(10).ok(),
                compliance_pct: row.get::<f64>(11).ok(),
                impediment_count: row.get::<i64>(12).ok(),
                calculated_at: row.get::<String>(13).ok(),
            });
        }
        Ok(snapshots)
    }

    #[tauri::command]
    pub async fn save_performance(state: State<'_, Mutex<String>>, snap: PerformanceSnapshot) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT INTO performance_snapshots (user_id, initiative_id, period, planned_sp, completed_sp, planned_hours, actual_hours, velocity, spi, cpi, compliance_pct, impediment_count) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)",
            libsql::params![
                snap.user_id, snap.initiative_id, snap.period,
                snap.planned_sp, snap.completed_sp,
                snap.planned_hours, snap.actual_hours,
                snap.velocity, snap.spi, snap.cpi,
                snap.compliance_pct, snap.impediment_count,
            ],
        ).await.map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    // ────────────────────────────────────────────
    // Epics
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_epics(state: State<'_, Mutex<String>>, initiative_id: i64) -> Result<Vec<Epic>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, initiative_id, code, name, description, status, client_taker, stimated_start_date, stimated_end_date, start_date, end_date, created_at, updated_at FROM epics WHERE initiative_id = ?1 ORDER BY created_at DESC", libsql::params![initiative_id])
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut epics = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            epics.push(Epic {
                id: row.get::<i64>(0).unwrap_or(0),
                initiative_id: row.get::<i64>(1).unwrap_or(0),
                code: row.get::<String>(2).ok(),
                name: row.get::<String>(3).unwrap_or_default(),
                description: row.get::<String>(4).ok(),
                status: row.get::<String>(5).ok(),
                client_taker: row.get::<i64>(6).ok(),
                stimated_start_date: row.get::<String>(7).ok(),
                stimated_end_date: row.get::<String>(8).ok(),
                start_date: row.get::<String>(9).ok(),
                end_date: row.get::<String>(10).ok(),
                created_at: row.get::<String>(11).ok(),
                updated_at: row.get::<String>(12).ok(),
                item_type: None,
                hierarchy_path: None,
            });
        }
        Ok(epics)
    }

    #[tauri::command]
    pub async fn save_epic(state: State<'_, Mutex<String>>, epic: Epic) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO epics (id, initiative_id, code, name, description, status, client_taker, stimated_start_date, stimated_end_date, start_date, end_date, item_type, hierarchy_path, updated_at)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, datetime('now'))",
            libsql::params![
                epic.id, epic.initiative_id, epic.code, epic.name, epic.description,
                epic.status, epic.client_taker, epic.stimated_start_date,
                epic.stimated_end_date, epic.start_date, epic.end_date,
                epic.item_type, epic.hierarchy_path,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn delete_epic(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM user_stories WHERE epic_id = ?1", libsql::params![id])
            .await.map_err(|e| format!("Delete HUs: {e}"))?;

        conn.execute("DELETE FROM epics WHERE id = ?1", libsql::params![id])
            .await.map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // User Stories
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_user_stories(state: State<'_, Mutex<String>>, initiative_id: i64, epic_id: Option<i64>) -> Result<Vec<UserStory>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let query = if epic_id.is_some() {
            "SELECT id, initiative_id, epic_id, code, title, description, acceptance_criteria, status, priority, story_points, estimated_hours, actual_hours, assigned_to, sprint, has_impairment, item_type, hierarchy_path, parent_code, created_at FROM user_stories WHERE initiative_id = ?1 AND epic_id = ?2 ORDER BY created_at DESC"
        } else {
            "SELECT id, initiative_id, epic_id, code, title, description, acceptance_criteria, status, priority, story_points, estimated_hours, actual_hours, assigned_to, sprint, has_impairment, item_type, hierarchy_path, parent_code, created_at FROM user_stories WHERE initiative_id = ?1 ORDER BY created_at DESC"
        };

        let mut rows = if let Some(eid) = epic_id {
            conn.query(query, libsql::params![initiative_id, eid]).await.map_err(|e| format!("Query: {e}"))?
        } else {
            conn.query(query, libsql::params![initiative_id]).await.map_err(|e| format!("Query: {e}"))?
        };

        let mut stories = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            stories.push(UserStory {
                id: row.get::<i64>(0).unwrap_or(0),
                initiative_id: row.get::<i64>(1).unwrap_or(0),
                epic_id: row.get::<i64>(2).ok(),
                code: row.get::<String>(3).ok(),
                title: row.get::<String>(4).unwrap_or_default(),
                description: row.get::<String>(5).ok(),
                acceptance_criteria: row.get::<String>(6).ok(),
                status: row.get::<String>(7).ok(),
                priority: row.get::<String>(8).ok(),
                story_points: row.get::<f64>(9).ok(),
                estimated_hours: row.get::<f64>(10).ok(),
                actual_hours: row.get::<f64>(11).ok(),
                assigned_to: row.get::<i64>(12).ok(),
                sprint: row.get::<String>(13).ok(),
                has_impairment: row.get::<i64>(14).unwrap_or(0) != 0,
                item_type: row.get::<String>(15).ok(),
                hierarchy_path: row.get::<String>(16).ok(),
                parent_code: row.get::<String>(17).ok(),
                created_at: row.get::<String>(18).ok(),
            });
        }
        Ok(stories)
    }

    #[tauri::command]
    pub async fn save_user_story(state: State<'_, Mutex<String>>, story: UserStory) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO user_stories (id, initiative_id, epic_id, code, title, description, acceptance_criteria, status, priority, story_points, estimated_hours, actual_hours, assigned_to, sprint, has_impairment, item_type, hierarchy_path, parent_code)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
            libsql::params![
                story.id, story.initiative_id, story.epic_id,
                story.code, story.title, story.description,
                story.acceptance_criteria, story.status, story.priority,
                story.story_points, story.estimated_hours,
                story.actual_hours, story.assigned_to,
                story.sprint, story.has_impairment,
                story.item_type, story.hierarchy_path, story.parent_code,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn delete_user_story(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM user_stories WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // Impairments
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_impairments(state: State<'_, Mutex<String>>, hu_id: i64) -> Result<Vec<Impairment>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let mut rows = conn
            .query("SELECT id, hu_id, responsible, pending_activity, pending_type, type_impediment, delivery_date, status, created_at, updated_at FROM impairments WHERE hu_id = ?1 ORDER BY created_at DESC", libsql::params![hu_id])
            .await
            .map_err(|e| format!("Query: {e}"))?;

        let mut items = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            items.push(Impairment {
                id: row.get::<i64>(0).unwrap_or(0),
                hu_id: row.get::<i64>(1).unwrap_or(0),
                responsible: row.get::<String>(2).ok(),
                pending_activity: row.get::<String>(3).ok(),
                pending_type: row.get::<String>(4).ok(),
                type_impediment: row.get::<i64>(5).unwrap_or(0) != 0,
                delivery_date: row.get::<String>(6).ok(),
                status: row.get::<i64>(7).unwrap_or(0) != 0,
                created_at: row.get::<String>(8).ok(),
                updated_at: row.get::<String>(9).ok(),
            });
        }
        Ok(items)
    }

    #[tauri::command]
    pub async fn save_impairment(state: State<'_, Mutex<String>>, imp: Impairment) -> Result<i64, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO impairments (id, hu_id, responsible, pending_activity, pending_type, type_impediment, delivery_date, status)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
            libsql::params![
                imp.id, imp.hu_id, imp.responsible, imp.pending_activity,
                imp.pending_type, imp.type_impediment, imp.delivery_date, imp.status,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(conn.last_insert_rowid())
    }

    #[tauri::command]
    pub async fn delete_impairment(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM impairments WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }

    // ────────────────────────────────────────────
    // HU Drafts (local, pre-KAPPA)
    // ────────────────────────────────────────────

    #[tauri::command]
    pub async fn get_hu_drafts(state: State<'_, Mutex<String>>, initiative_id: Option<i64>) -> Result<Vec<HuDraft>, String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        let query = if initiative_id.is_some() {
            "SELECT id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, created_at, updated_at FROM hu_drafts WHERE initiative_id = ?1 ORDER BY created_at DESC"
        } else {
            "SELECT id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, created_at, updated_at FROM hu_drafts ORDER BY created_at DESC"
        };

        let mut rows = if let Some(pid) = initiative_id {
            conn.query(query, libsql::params![pid]).await.map_err(|e| format!("Query: {e}"))?
        } else {
            conn.query(query, ()).await.map_err(|e| format!("Query: {e}"))?
        };

        let mut drafts = Vec::new();
        while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? {
            drafts.push(HuDraft {
                id: row.get::<String>(0).unwrap_or_default(),
                initiative_id: row.get::<i64>(1).ok(),
                code: row.get::<String>(2).ok(),
                title: row.get::<String>(3).unwrap_or_default(),
                description: row.get::<String>(4).ok(),
                acceptance_criteria: row.get::<String>(5).ok(),
                item_type: row.get::<String>(6).ok(),
                hierarchy_path: row.get::<String>(7).ok(),
                story_points: row.get::<f64>(8).ok(),
                sprint: row.get::<String>(9).ok(),
                assigned_to: row.get::<String>(10).ok(),
                sync_status: row.get::<String>(11).unwrap_or_else(|_| "draft".into()),
                kappa_id: row.get::<i64>(12).ok(),
                created_at: row.get::<String>(13).ok(),
                updated_at: row.get::<String>(14).ok(),
            });
        }
        Ok(drafts)
    }

    #[tauri::command]
    pub async fn save_hu_draft(state: State<'_, Mutex<String>>, draft: HuDraft) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute(
            "INSERT OR REPLACE INTO hu_drafts (id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, updated_at)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, datetime('now'))",
            libsql::params![
                draft.id, draft.initiative_id, draft.code, draft.title,
                draft.description, draft.acceptance_criteria, draft.item_type,
                draft.hierarchy_path, draft.story_points, draft.sprint,
                draft.assigned_to, draft.sync_status, draft.kappa_id,
            ],
        )
        .await
        .map_err(|e| format!("Insert: {e}"))?;

        Ok(())
    }

    #[tauri::command]
    pub async fn delete_hu_draft(state: State<'_, Mutex<String>>, id: String) -> Result<(), String> {
        let db_path = state.lock().map_err(|e| e.to_string())?.clone();
        let conn = get_conn(&db_path).await?;

        conn.execute("DELETE FROM hu_drafts WHERE id = ?1", libsql::params![id])
            .await
            .map_err(|e| format!("Delete: {e}"))?;

        Ok(())
    }
}
