use lore::gitlab::transformers::merge_request::transform_merge_request;
use lore::gitlab::types::{GitLabAuthor, GitLabMergeRequest, GitLabReferences, GitLabReviewer};

fn make_test_mr() -> GitLabMergeRequest {
    GitLabMergeRequest {
        id: 12345,
        iid: 42,
        project_id: 100,
        title: "Add user authentication".to_string(),
        description: Some("Implements JWT auth flow".to_string()),
        state: "merged".to_string(),
        draft: false,
        work_in_progress: false,
        source_branch: "feature/auth".to_string(),
        target_branch: "main".to_string(),
        sha: Some("abc123def456".to_string()),
        references: Some(GitLabReferences {
            short: "!42".to_string(),
            full: "group/project!42".to_string(),
        }),
        detailed_merge_status: Some("mergeable".to_string()),
        merge_status_legacy: Some("can_be_merged".to_string()),
        created_at: "2024-01-15T10:00:00.000Z".to_string(),
        updated_at: "2024-01-20T14:30:00.000Z".to_string(),
        merged_at: Some("2024-01-20T14:30:00.000Z".to_string()),
        closed_at: None,
        author: GitLabAuthor {
            id: 1,
            username: "johndoe".to_string(),
            name: "John Doe".to_string(),
        },
        merge_user: Some(GitLabAuthor {
            id: 2,
            username: "janedoe".to_string(),
            name: "Jane Doe".to_string(),
        }),
        merged_by: Some(GitLabAuthor {
            id: 2,
            username: "janedoe".to_string(),
            name: "Jane Doe".to_string(),
        }),
        labels: vec!["enhancement".to_string(), "auth".to_string()],
        assignees: vec![GitLabAuthor {
            id: 3,
            username: "bob".to_string(),
            name: "Bob Smith".to_string(),
        }],
        reviewers: vec![GitLabReviewer {
            id: 4,
            username: "alice".to_string(),
            name: "Alice Wong".to_string(),
        }],
        web_url: "https://gitlab.example.com/group/project/-/merge_requests/42".to_string(),
        merge_commit_sha: Some("merge1234567890".to_string()),
        squash_commit_sha: None,
    }
}

#[test]
fn transforms_mr_with_all_fields() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(result.merge_request.gitlab_id, 12345);
    assert_eq!(result.merge_request.iid, 42);
    assert_eq!(result.merge_request.project_id, 200);
    assert_eq!(result.merge_request.title, "Add user authentication");
    assert_eq!(
        result.merge_request.description,
        Some("Implements JWT auth flow".to_string())
    );
    assert_eq!(result.merge_request.state, "merged");
    assert!(!result.merge_request.draft);
    assert_eq!(result.merge_request.author_username, "johndoe");
    assert_eq!(result.merge_request.source_branch, "feature/auth");
    assert_eq!(result.merge_request.target_branch, "main");
    assert_eq!(
        result.merge_request.head_sha,
        Some("abc123def456".to_string())
    );
    assert_eq!(
        result.merge_request.references_short,
        Some("!42".to_string())
    );
    assert_eq!(
        result.merge_request.references_full,
        Some("group/project!42".to_string())
    );
    assert_eq!(
        result.merge_request.detailed_merge_status,
        Some("mergeable".to_string())
    );
    assert_eq!(
        result.merge_request.merge_user_username,
        Some("janedoe".to_string())
    );
    assert_eq!(
        result.merge_request.web_url,
        "https://gitlab.example.com/group/project/-/merge_requests/42"
    );
}

#[test]
fn preserves_commit_shas() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(
        result.merge_request.merge_commit_sha,
        Some("merge1234567890".to_string())
    );
    assert!(result.merge_request.squash_commit_sha.is_none());
}

#[test]
fn parses_timestamps_to_ms_epoch() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(result.merge_request.created_at, 1705312800000);
    assert_eq!(result.merge_request.updated_at, 1705761000000);
    assert_eq!(result.merge_request.merged_at, Some(1705761000000));
}

#[test]
fn handles_timezone_offset_timestamps() {
    let mut mr = make_test_mr();
    mr.created_at = "2024-01-15T05:00:00-05:00".to_string();

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(result.merge_request.created_at, 1705312800000);
}

#[test]
fn sets_last_seen_at_to_current_time() {
    let mr = make_test_mr();
    let before = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_millis() as i64;

    let result = transform_merge_request(&mr, 200).unwrap();

    let after = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_millis() as i64;

    assert!(result.merge_request.last_seen_at >= before);
    assert!(result.merge_request.last_seen_at <= after);
}

#[test]
fn extracts_label_names() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(result.label_names.len(), 2);
    assert_eq!(result.label_names[0], "enhancement");
    assert_eq!(result.label_names[1], "auth");
}

#[test]
fn handles_empty_labels() {
    let mut mr = make_test_mr();
    mr.labels = vec![];

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.label_names.is_empty());
}

#[test]
fn extracts_assignee_usernames() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(result.assignee_usernames.len(), 1);
    assert_eq!(result.assignee_usernames[0], "bob");
}

#[test]
fn extracts_reviewer_usernames() {
    let mr = make_test_mr();
    let result = transform_merge_request(&mr, 200).unwrap();

    assert_eq!(result.reviewer_usernames.len(), 1);
    assert_eq!(result.reviewer_usernames[0], "alice");
}

#[test]
fn handles_empty_assignees_and_reviewers() {
    let mut mr = make_test_mr();
    mr.assignees = vec![];
    mr.reviewers = vec![];

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.assignee_usernames.is_empty());
    assert!(result.reviewer_usernames.is_empty());
}

#[test]
fn draft_prefers_draft_field() {
    let mut mr = make_test_mr();
    mr.draft = true;
    mr.work_in_progress = false;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.draft);
}

#[test]
fn draft_falls_back_to_work_in_progress() {
    let mut mr = make_test_mr();
    mr.draft = false;
    mr.work_in_progress = true;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.draft);
}

#[test]
fn draft_false_when_both_false() {
    let mut mr = make_test_mr();
    mr.draft = false;
    mr.work_in_progress = false;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(!result.merge_request.draft);
}

#[test]
fn detailed_merge_status_prefers_non_legacy() {
    let mut mr = make_test_mr();
    mr.detailed_merge_status = Some("checking".to_string());
    mr.merge_status_legacy = Some("can_be_merged".to_string());

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(
        result.merge_request.detailed_merge_status,
        Some("checking".to_string())
    );
}

#[test]
fn detailed_merge_status_falls_back_to_legacy() {
    let mut mr = make_test_mr();
    mr.detailed_merge_status = None;
    mr.merge_status_legacy = Some("can_be_merged".to_string());

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(
        result.merge_request.detailed_merge_status,
        Some("can_be_merged".to_string())
    );
}

#[test]
fn merge_user_prefers_merge_user_field() {
    let mut mr = make_test_mr();
    mr.merge_user = Some(GitLabAuthor {
        id: 10,
        username: "merge_user_name".to_string(),
        name: "Merge User".to_string(),
    });
    mr.merged_by = Some(GitLabAuthor {
        id: 11,
        username: "merged_by_name".to_string(),
        name: "Merged By".to_string(),
    });

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(
        result.merge_request.merge_user_username,
        Some("merge_user_name".to_string())
    );
}

#[test]
fn merge_user_falls_back_to_merged_by() {
    let mut mr = make_test_mr();
    mr.merge_user = None;
    mr.merged_by = Some(GitLabAuthor {
        id: 11,
        username: "merged_by_name".to_string(),
        name: "Merged By".to_string(),
    });

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(
        result.merge_request.merge_user_username,
        Some("merged_by_name".to_string())
    );
}

#[test]
fn handles_missing_references() {
    let mut mr = make_test_mr();
    mr.references = None;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.references_short.is_none());
    assert!(result.merge_request.references_full.is_none());
}

#[test]
fn handles_missing_sha() {
    let mut mr = make_test_mr();
    mr.sha = None;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.head_sha.is_none());
}

#[test]
fn handles_missing_description() {
    let mut mr = make_test_mr();
    mr.description = None;

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.description.is_none());
}

#[test]
fn handles_closed_at_timestamp() {
    let mut mr = make_test_mr();
    mr.state = "closed".to_string();
    mr.merged_at = None;
    mr.closed_at = Some("2024-01-18T12:00:00.000Z".to_string());

    let result = transform_merge_request(&mr, 200).unwrap();
    assert!(result.merge_request.merged_at.is_none());
    assert_eq!(result.merge_request.closed_at, Some(1705579200000));
}

#[test]
fn passes_through_locked_state() {
    let mut mr = make_test_mr();
    mr.state = "locked".to_string();

    let result = transform_merge_request(&mr, 200).unwrap();
    assert_eq!(result.merge_request.state, "locked");
}

#[test]
fn returns_error_for_invalid_created_at() {
    let mut mr = make_test_mr();
    mr.created_at = "not-a-timestamp".to_string();

    let result = transform_merge_request(&mr, 200);
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(err.contains("not-a-timestamp"));
}

#[test]
fn returns_error_for_invalid_updated_at() {
    let mut mr = make_test_mr();
    mr.updated_at = "invalid".to_string();

    let result = transform_merge_request(&mr, 200);
    assert!(result.is_err());
}

#[test]
fn returns_error_for_invalid_merged_at() {
    let mut mr = make_test_mr();
    mr.merged_at = Some("bad-timestamp".to_string());

    let result = transform_merge_request(&mr, 200);
    assert!(result.is_err());
}

#[test]
fn returns_error_for_invalid_closed_at() {
    let mut mr = make_test_mr();
    mr.closed_at = Some("garbage".to_string());

    let result = transform_merge_request(&mr, 200);
    assert!(result.is_err());
}
