use super::*;
use crate::core::db::{create_connection, run_migrations};
use std::path::Path;

fn setup() -> Connection {
    let conn = create_connection(Path::new(":memory:")).unwrap();
    run_migrations(&conn).unwrap();

    // Insert a test project
    conn.execute(
        "INSERT INTO projects (gitlab_project_id, path_with_namespace, web_url) VALUES (1, 'group/repo', 'https://gitlab.com/group/repo')",
        [],
    ).unwrap();

    // Insert a test MR
    conn.execute(
        "INSERT INTO merge_requests (gitlab_id, iid, project_id, title, state, draft, source_branch, target_branch, author_username, created_at, updated_at, last_seen_at) \
         VALUES (100, 1, 1, 'Test MR', 'merged', 0, 'feature', 'main', 'testuser', 1000, 2000, 3000)",
        [],
    ).unwrap();

    conn
}

#[test]
fn test_derive_change_type_added() {
    let diff = GitLabMrDiff {
        old_path: String::new(),
        new_path: "src/new.rs".to_string(),
        new_file: true,
        renamed_file: false,
        deleted_file: false,
    };
    assert_eq!(derive_change_type(&diff), "added");
}

#[test]
fn test_derive_change_type_renamed() {
    let diff = GitLabMrDiff {
        old_path: "src/old.rs".to_string(),
        new_path: "src/new.rs".to_string(),
        new_file: false,
        renamed_file: true,
        deleted_file: false,
    };
    assert_eq!(derive_change_type(&diff), "renamed");
}

#[test]
fn test_derive_change_type_deleted() {
    let diff = GitLabMrDiff {
        old_path: "src/gone.rs".to_string(),
        new_path: "src/gone.rs".to_string(),
        new_file: false,
        renamed_file: false,
        deleted_file: true,
    };
    assert_eq!(derive_change_type(&diff), "deleted");
}

#[test]
fn test_derive_change_type_modified() {
    let diff = GitLabMrDiff {
        old_path: "src/lib.rs".to_string(),
        new_path: "src/lib.rs".to_string(),
        new_file: false,
        renamed_file: false,
        deleted_file: false,
    };
    assert_eq!(derive_change_type(&diff), "modified");
}

#[test]
fn test_upsert_inserts_file_changes() {
    let conn = setup();
    let diffs = [
        GitLabMrDiff {
            old_path: String::new(),
            new_path: "src/new.rs".to_string(),
            new_file: true,
            renamed_file: false,
            deleted_file: false,
        },
        GitLabMrDiff {
            old_path: "src/lib.rs".to_string(),
            new_path: "src/lib.rs".to_string(),
            new_file: false,
            renamed_file: false,
            deleted_file: false,
        },
    ];

    let inserted = upsert_mr_file_changes(&conn, 1, 1, &diffs).unwrap();
    assert_eq!(inserted, 2);

    let count: i64 = conn
        .query_row(
            "SELECT COUNT(*) FROM mr_file_changes WHERE merge_request_id = 1",
            [],
            |r| r.get(0),
        )
        .unwrap();
    assert_eq!(count, 2);
}

#[test]
fn test_upsert_replaces_existing() {
    let conn = setup();
    let diffs_v1 = [GitLabMrDiff {
        old_path: String::new(),
        new_path: "src/old.rs".to_string(),
        new_file: true,
        renamed_file: false,
        deleted_file: false,
    }];
    upsert_mr_file_changes(&conn, 1, 1, &diffs_v1).unwrap();

    let diffs_v2 = [
        GitLabMrDiff {
            old_path: "src/a.rs".to_string(),
            new_path: "src/a.rs".to_string(),
            new_file: false,
            renamed_file: false,
            deleted_file: false,
        },
        GitLabMrDiff {
            old_path: "src/b.rs".to_string(),
            new_path: "src/b.rs".to_string(),
            new_file: false,
            renamed_file: false,
            deleted_file: false,
        },
    ];
    let inserted = upsert_mr_file_changes(&conn, 1, 1, &diffs_v2).unwrap();
    assert_eq!(inserted, 2);

    let count: i64 = conn
        .query_row(
            "SELECT COUNT(*) FROM mr_file_changes WHERE merge_request_id = 1",
            [],
            |r| r.get(0),
        )
        .unwrap();
    assert_eq!(count, 2);

    // The old "src/old.rs" should be gone
    let old_count: i64 = conn
        .query_row(
            "SELECT COUNT(*) FROM mr_file_changes WHERE new_path = 'src/old.rs'",
            [],
            |r| r.get(0),
        )
        .unwrap();
    assert_eq!(old_count, 0);
}

#[test]
fn test_renamed_stores_old_path() {
    let conn = setup();
    let diffs = [GitLabMrDiff {
        old_path: "src/old_name.rs".to_string(),
        new_path: "src/new_name.rs".to_string(),
        new_file: false,
        renamed_file: true,
        deleted_file: false,
    }];

    upsert_mr_file_changes(&conn, 1, 1, &diffs).unwrap();

    let (old_path, change_type): (Option<String>, String) = conn
        .query_row(
            "SELECT old_path, change_type FROM mr_file_changes WHERE new_path = 'src/new_name.rs'",
            [],
            |r| Ok((r.get(0)?, r.get(1)?)),
        )
        .unwrap();
    assert_eq!(old_path.as_deref(), Some("src/old_name.rs"));
    assert_eq!(change_type, "renamed");
}

#[test]
fn test_non_renamed_has_null_old_path() {
    let conn = setup();
    let diffs = [GitLabMrDiff {
        old_path: "src/lib.rs".to_string(),
        new_path: "src/lib.rs".to_string(),
        new_file: false,
        renamed_file: false,
        deleted_file: false,
    }];

    upsert_mr_file_changes(&conn, 1, 1, &diffs).unwrap();

    let old_path: Option<String> = conn
        .query_row(
            "SELECT old_path FROM mr_file_changes WHERE new_path = 'src/lib.rs'",
            [],
            |r| r.get(0),
        )
        .unwrap();
    assert!(old_path.is_none());
}
