use lore::gitlab::types::{
    GitLabAuthor, GitLabDiscussion, GitLabIssue, GitLabIssueRef, GitLabLabelEvent, GitLabLabelRef,
    GitLabMergeRequest, GitLabMergeRequestRef, GitLabMilestone, GitLabMilestoneEvent,
    GitLabMilestoneRef, GitLabNote, GitLabNotePosition, GitLabReferences, GitLabReviewer,
    GitLabStateEvent,
};

#[test]
fn deserializes_gitlab_issue_from_json() {
    let json = r#"{
        "id": 123456,
        "iid": 42,
        "project_id": 789,
        "title": "Fix authentication bug",
        "description": "The auth flow is broken when...",
        "state": "opened",
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-16T14:20:00.000Z",
        "closed_at": null,
        "author": {
            "id": 1,
            "username": "johndoe",
            "name": "John Doe"
        },
        "labels": ["bug", "auth", "priority::high"],
        "web_url": "https://gitlab.example.com/group/project/-/issues/42"
    }"#;

    let issue: GitLabIssue = serde_json::from_str(json).expect("Failed to deserialize issue");

    assert_eq!(issue.id, 123456);
    assert_eq!(issue.iid, 42);
    assert_eq!(issue.project_id, 789);
    assert_eq!(issue.title, "Fix authentication bug");
    assert_eq!(
        issue.description,
        Some("The auth flow is broken when...".to_string())
    );
    assert_eq!(issue.state, "opened");
    assert_eq!(issue.author.username, "johndoe");
    assert_eq!(issue.labels, vec!["bug", "auth", "priority::high"]);
    assert_eq!(
        issue.web_url,
        "https://gitlab.example.com/group/project/-/issues/42"
    );
}

#[test]
fn deserializes_gitlab_issue_with_empty_labels() {
    let json = r#"{
        "id": 123,
        "iid": 1,
        "project_id": 456,
        "title": "Test issue",
        "description": null,
        "state": "closed",
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-02T00:00:00.000Z",
        "closed_at": "2024-01-02T00:00:00.000Z",
        "author": {
            "id": 1,
            "username": "testuser",
            "name": "Test User"
        },
        "labels": [],
        "web_url": "https://gitlab.example.com/group/project/-/issues/1"
    }"#;

    let issue: GitLabIssue = serde_json::from_str(json).expect("Failed to deserialize issue");

    assert_eq!(issue.description, None);
    assert!(issue.labels.is_empty());
    assert_eq!(
        issue.closed_at,
        Some("2024-01-02T00:00:00.000Z".to_string())
    );
}

#[test]
fn deserializes_gitlab_discussion_from_json() {
    let json = r#"{
        "id": "6a9c1750b37d513de9f7ef7f4d2d2b3a5b2c1d0e",
        "individual_note": false,
        "notes": [
            {
                "id": 12345,
                "type": "DiscussionNote",
                "body": "I think we should refactor this.",
                "author": {
                    "id": 1,
                    "username": "johndoe",
                    "name": "John Doe"
                },
                "created_at": "2024-03-15T10:30:00.000Z",
                "updated_at": "2024-03-15T10:30:00.000Z",
                "system": false,
                "resolvable": true,
                "resolved": false,
                "resolved_by": null,
                "resolved_at": null,
                "position": null
            }
        ]
    }"#;

    let discussion: GitLabDiscussion =
        serde_json::from_str(json).expect("Failed to deserialize discussion");

    assert_eq!(discussion.id, "6a9c1750b37d513de9f7ef7f4d2d2b3a5b2c1d0e");
    assert!(!discussion.individual_note);
    assert_eq!(discussion.notes.len(), 1);
    assert_eq!(discussion.notes[0].body, "I think we should refactor this.");
}

#[test]
fn deserializes_individual_note_discussion() {
    let json = r#"{
        "id": "abc123",
        "individual_note": true,
        "notes": [
            {
                "id": 99999,
                "type": null,
                "body": "A simple standalone comment",
                "author": {
                    "id": 2,
                    "username": "janedoe",
                    "name": "Jane Doe"
                },
                "created_at": "2024-03-15T10:30:00.000Z",
                "updated_at": "2024-03-15T10:30:00.000Z",
                "system": false
            }
        ]
    }"#;

    let discussion: GitLabDiscussion =
        serde_json::from_str(json).expect("Failed to deserialize discussion");

    assert!(discussion.individual_note);
    assert_eq!(discussion.notes.len(), 1);
}

#[test]
fn handles_null_note_type() {
    let json = r#"{
        "id": 12345,
        "type": null,
        "body": "A note without a type",
        "author": {
            "id": 1,
            "username": "user",
            "name": "User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-15T10:30:00.000Z",
        "system": false
    }"#;

    let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize note");

    assert!(note.note_type.is_none());
}

#[test]
fn handles_diffnote_type() {
    let json = r#"{
        "id": 12345,
        "type": "DiffNote",
        "body": "This line needs to be changed",
        "author": {
            "id": 1,
            "username": "reviewer",
            "name": "Reviewer"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-15T10:30:00.000Z",
        "system": false,
        "resolvable": true,
        "resolved": true,
        "resolved_by": {
            "id": 2,
            "username": "author",
            "name": "Author"
        },
        "resolved_at": "2024-03-16T08:00:00.000Z",
        "position": {
            "old_path": "src/auth/login.rs",
            "new_path": "src/auth/login.rs",
            "old_line": 42,
            "new_line": 45
        }
    }"#;

    let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize DiffNote");

    assert_eq!(note.note_type, Some("DiffNote".to_string()));
    assert!(note.resolvable);
    assert!(note.resolved);
    assert!(note.resolved_by.is_some());
    assert_eq!(note.resolved_by.as_ref().unwrap().username, "author");
    assert!(note.position.is_some());

    let pos = note.position.unwrap();
    assert_eq!(pos.old_path, Some("src/auth/login.rs".to_string()));
    assert_eq!(pos.new_path, Some("src/auth/login.rs".to_string()));
    assert_eq!(pos.old_line, Some(42));
    assert_eq!(pos.new_line, Some(45));
}

#[test]
fn handles_missing_resolvable_field() {
    let json = r#"{
        "id": 12345,
        "type": null,
        "body": "A simple note",
        "author": {
            "id": 1,
            "username": "user",
            "name": "User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-15T10:30:00.000Z",
        "system": false
    }"#;

    let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize note");

    assert!(!note.resolvable);
    assert!(!note.resolved);
}

#[test]
fn deserializes_system_note() {
    let json = r#"{
        "id": 12345,
        "type": null,
        "body": "added ~bug label",
        "author": {
            "id": 1,
            "username": "user",
            "name": "User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-15T10:30:00.000Z",
        "system": true
    }"#;

    let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize system note");

    assert!(note.system);
    assert_eq!(note.body, "added ~bug label");
}

#[test]
fn deserializes_note_position_with_partial_fields() {
    let json = r#"{
        "old_path": null,
        "new_path": "src/new_file.rs",
        "old_line": null,
        "new_line": 10
    }"#;

    let pos: GitLabNotePosition =
        serde_json::from_str(json).expect("Failed to deserialize position");

    assert!(pos.old_path.is_none());
    assert_eq!(pos.new_path, Some("src/new_file.rs".to_string()));
    assert!(pos.old_line.is_none());
    assert_eq!(pos.new_line, Some(10));
}

#[test]
fn deserializes_gitlab_author() {
    let json = r#"{
        "id": 42,
        "username": "developer",
        "name": "Dev User"
    }"#;

    let author: GitLabAuthor = serde_json::from_str(json).expect("Failed to deserialize author");

    assert_eq!(author.id, 42);
    assert_eq!(author.username, "developer");
    assert_eq!(author.name, "Dev User");
}

#[test]
fn deserializes_gitlab_issue_with_assignees() {
    let json = r#"{
        "id": 123456,
        "iid": 42,
        "project_id": 789,
        "title": "Fix authentication bug",
        "description": "The auth flow is broken",
        "state": "opened",
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-16T14:20:00.000Z",
        "closed_at": null,
        "author": {
            "id": 1,
            "username": "johndoe",
            "name": "John Doe"
        },
        "assignees": [
            {
                "id": 2,
                "username": "alice",
                "name": "Alice Smith"
            },
            {
                "id": 3,
                "username": "bob",
                "name": "Bob Jones"
            }
        ],
        "labels": ["bug"],
        "milestone": null,
        "due_date": null,
        "web_url": "https://gitlab.example.com/group/project/-/issues/42"
    }"#;

    let issue: GitLabIssue = serde_json::from_str(json).expect("Failed to deserialize issue");

    assert_eq!(issue.assignees.len(), 2);
    assert_eq!(issue.assignees[0].username, "alice");
    assert_eq!(issue.assignees[1].username, "bob");
}

#[test]
fn deserializes_gitlab_issue_with_milestone() {
    let json = r#"{
        "id": 123456,
        "iid": 42,
        "project_id": 789,
        "title": "Fix authentication bug",
        "description": null,
        "state": "opened",
        "created_at": "2024-03-15T10:30:00.000Z",
        "updated_at": "2024-03-16T14:20:00.000Z",
        "closed_at": null,
        "author": {
            "id": 1,
            "username": "johndoe",
            "name": "John Doe"
        },
        "assignees": [],
        "labels": [],
        "milestone": {
            "id": 500,
            "iid": 5,
            "project_id": 789,
            "title": "v1.0",
            "description": "First release",
            "state": "active",
            "due_date": "2024-04-01",
            "web_url": "https://gitlab.example.com/group/project/-/milestones/5"
        },
        "due_date": "2024-03-31",
        "web_url": "https://gitlab.example.com/group/project/-/issues/42"
    }"#;

    let issue: GitLabIssue = serde_json::from_str(json).expect("Failed to deserialize issue");

    assert!(issue.milestone.is_some());
    let milestone = issue.milestone.unwrap();
    assert_eq!(milestone.id, 500);
    assert_eq!(milestone.title, "v1.0");
    assert_eq!(milestone.due_date, Some("2024-04-01".to_string()));
    assert_eq!(issue.due_date, Some("2024-03-31".to_string()));
}

#[test]
fn deserializes_gitlab_milestone() {
    let json = r#"{
        "id": 500,
        "iid": 5,
        "project_id": 789,
        "title": "v1.0",
        "description": "First release milestone",
        "state": "active",
        "due_date": "2024-04-01",
        "web_url": "https://gitlab.example.com/group/project/-/milestones/5"
    }"#;

    let milestone: GitLabMilestone =
        serde_json::from_str(json).expect("Failed to deserialize milestone");

    assert_eq!(milestone.id, 500);
    assert_eq!(milestone.iid, 5);
    assert_eq!(milestone.project_id, Some(789));
    assert_eq!(milestone.title, "v1.0");
    assert_eq!(
        milestone.description,
        Some("First release milestone".to_string())
    );
    assert_eq!(milestone.state, Some("active".to_string()));
    assert_eq!(milestone.due_date, Some("2024-04-01".to_string()));
}

#[test]
fn deserializes_gitlab_merge_request_from_fixture() {
    let json = include_str!("fixtures/gitlab_merge_request.json");
    let mr: GitLabMergeRequest =
        serde_json::from_str(json).expect("Failed to deserialize merge request");

    assert_eq!(mr.id, 12345);
    assert_eq!(mr.iid, 42);
    assert_eq!(mr.project_id, 100);
    assert_eq!(mr.title, "Add user authentication");
    assert_eq!(mr.description, Some("Implements JWT auth flow".to_string()));
    assert_eq!(mr.state, "merged");
    assert!(!mr.draft);
    assert!(!mr.work_in_progress);
    assert_eq!(mr.source_branch, "feature/auth");
    assert_eq!(mr.target_branch, "main");
    assert_eq!(mr.sha, Some("abc123def456".to_string()));
    assert_eq!(mr.detailed_merge_status, Some("mergeable".to_string()));
    assert_eq!(mr.merge_status_legacy, Some("can_be_merged".to_string()));
    assert_eq!(mr.author.username, "johndoe");
    assert!(mr.merge_user.is_some());
    assert_eq!(mr.merge_user.as_ref().unwrap().username, "janedoe");
    assert!(mr.merged_by.is_some());
    assert_eq!(mr.labels, vec!["enhancement", "auth"]);
    assert_eq!(mr.assignees.len(), 1);
    assert_eq!(mr.assignees[0].username, "bob");
    assert_eq!(mr.reviewers.len(), 1);
    assert_eq!(mr.reviewers[0].username, "alice");
}

#[test]
fn deserializes_gitlab_merge_request_with_references() {
    let json = include_str!("fixtures/gitlab_merge_request.json");
    let mr: GitLabMergeRequest =
        serde_json::from_str(json).expect("Failed to deserialize merge request");

    assert!(mr.references.is_some());
    let refs = mr.references.unwrap();
    assert_eq!(refs.short, "!42");
    assert_eq!(refs.full, "group/project!42");
}

#[test]
fn deserializes_gitlab_merge_request_minimal() {
    let json = r#"{
        "id": 1,
        "iid": 1,
        "project_id": 1,
        "title": "Test MR",
        "state": "opened",
        "source_branch": "feature",
        "target_branch": "main",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z",
        "author": { "id": 1, "username": "user", "name": "User" },
        "web_url": "https://example.com/mr/1"
    }"#;

    let mr: GitLabMergeRequest =
        serde_json::from_str(json).expect("Failed to deserialize minimal MR");

    assert_eq!(mr.id, 1);
    assert!(mr.description.is_none());
    assert!(!mr.draft);
    assert!(!mr.work_in_progress);
    assert!(mr.sha.is_none());
    assert!(mr.references.is_none());
    assert!(mr.detailed_merge_status.is_none());
    assert!(mr.merge_status_legacy.is_none());
    assert!(mr.merged_at.is_none());
    assert!(mr.closed_at.is_none());
    assert!(mr.merge_user.is_none());
    assert!(mr.merged_by.is_none());
    assert!(mr.labels.is_empty());
    assert!(mr.assignees.is_empty());
    assert!(mr.reviewers.is_empty());
}

#[test]
fn deserializes_gitlab_merge_request_with_draft() {
    let json = r#"{
        "id": 1,
        "iid": 1,
        "project_id": 1,
        "title": "Draft MR",
        "state": "opened",
        "draft": true,
        "source_branch": "wip",
        "target_branch": "main",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z",
        "author": { "id": 1, "username": "user", "name": "User" },
        "web_url": "https://example.com/mr/1"
    }"#;

    let mr: GitLabMergeRequest =
        serde_json::from_str(json).expect("Failed to deserialize draft MR");

    assert!(mr.draft);
}

#[test]
fn deserializes_gitlab_merge_request_with_work_in_progress_fallback() {
    let json = r#"{
        "id": 1,
        "iid": 1,
        "project_id": 1,
        "title": "WIP MR",
        "state": "opened",
        "work_in_progress": true,
        "source_branch": "wip",
        "target_branch": "main",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z",
        "author": { "id": 1, "username": "user", "name": "User" },
        "web_url": "https://example.com/mr/1"
    }"#;

    let mr: GitLabMergeRequest = serde_json::from_str(json).expect("Failed to deserialize WIP MR");

    assert!(mr.work_in_progress);
    assert!(!mr.draft);
}

#[test]
fn deserializes_gitlab_merge_request_with_locked_state() {
    let json = r#"{
        "id": 1,
        "iid": 1,
        "project_id": 1,
        "title": "Merging MR",
        "state": "locked",
        "source_branch": "feature",
        "target_branch": "main",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z",
        "author": { "id": 1, "username": "user", "name": "User" },
        "web_url": "https://example.com/mr/1"
    }"#;

    let mr: GitLabMergeRequest =
        serde_json::from_str(json).expect("Failed to deserialize locked MR");

    assert_eq!(mr.state, "locked");
}

#[test]
fn deserializes_gitlab_reviewer() {
    let json = r#"{
        "id": 42,
        "username": "reviewer",
        "name": "Code Reviewer"
    }"#;

    let reviewer: GitLabReviewer =
        serde_json::from_str(json).expect("Failed to deserialize reviewer");

    assert_eq!(reviewer.id, 42);
    assert_eq!(reviewer.username, "reviewer");
    assert_eq!(reviewer.name, "Code Reviewer");
}

#[test]
fn deserializes_gitlab_references() {
    let json = r#"{
        "short": "!123",
        "full": "group/project!123"
    }"#;

    let refs: GitLabReferences =
        serde_json::from_str(json).expect("Failed to deserialize references");

    assert_eq!(refs.short, "!123");
    assert_eq!(refs.full, "group/project!123");
}

#[test]
fn deserializes_diffnote_position_with_sha_triplet() {
    let json = r#"{
        "old_path": "src/auth.rs",
        "new_path": "src/auth.rs",
        "old_line": 42,
        "new_line": 45,
        "position_type": "text",
        "base_sha": "abc123",
        "start_sha": "def456",
        "head_sha": "ghi789"
    }"#;

    let pos: GitLabNotePosition =
        serde_json::from_str(json).expect("Failed to deserialize position with SHA triplet");

    assert_eq!(pos.position_type, Some("text".to_string()));
    assert_eq!(pos.base_sha, Some("abc123".to_string()));
    assert_eq!(pos.start_sha, Some("def456".to_string()));
    assert_eq!(pos.head_sha, Some("ghi789".to_string()));
}

#[test]
fn deserializes_diffnote_position_with_line_range() {
    let json = r#"{
        "old_path": null,
        "new_path": "src/new.rs",
        "old_line": null,
        "new_line": 10,
        "position_type": "text",
        "line_range": {
            "start": {
                "line_code": "abc123_10_10",
                "type": "new",
                "old_line": null,
                "new_line": 10
            },
            "end": {
                "line_code": "abc123_15_15",
                "type": "new",
                "old_line": null,
                "new_line": 15
            }
        }
    }"#;

    let pos: GitLabNotePosition =
        serde_json::from_str(json).expect("Failed to deserialize position with line range");

    assert!(pos.line_range.is_some());
    let range = pos.line_range.unwrap();
    assert_eq!(range.start_line(), Some(10));
    assert_eq!(range.end_line(), Some(15));
}

#[test]
fn deserializes_state_event_closed_by_mr() {
    let json = r#"{
        "id": 1001,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "Issue",
        "resource_id": 555,
        "state": "closed",
        "source_commit": null,
        "source_merge_request": {
            "iid": 99,
            "title": "Fix the bug",
            "web_url": "https://gitlab.example.com/group/project/-/merge_requests/99"
        }
    }"#;

    let event: GitLabStateEvent =
        serde_json::from_str(json).expect("Failed to deserialize state event");

    assert_eq!(event.id, 1001);
    assert!(event.user.is_some());
    assert_eq!(event.user.as_ref().unwrap().username, "developer");
    assert_eq!(event.resource_type, "Issue");
    assert_eq!(event.resource_id, 555);
    assert_eq!(event.state, "closed");
    assert!(event.source_commit.is_none());
    assert!(event.source_merge_request.is_some());
    let mr_ref = event.source_merge_request.unwrap();
    assert_eq!(mr_ref.iid, 99);
    assert_eq!(mr_ref.title, Some("Fix the bug".to_string()));
}

#[test]
fn deserializes_state_event_simple_no_user() {
    let json = r#"{
        "id": 1002,
        "user": null,
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "MergeRequest",
        "resource_id": 777,
        "state": "merged",
        "source_commit": "abc123def456",
        "source_merge_request": null
    }"#;

    let event: GitLabStateEvent =
        serde_json::from_str(json).expect("Failed to deserialize state event without user");

    assert_eq!(event.id, 1002);
    assert!(event.user.is_none());
    assert_eq!(event.resource_type, "MergeRequest");
    assert_eq!(event.state, "merged");
    assert_eq!(event.source_commit, Some("abc123def456".to_string()));
    assert!(event.source_merge_request.is_none());
}

#[test]
fn deserializes_label_event_add() {
    let json = r##"{
        "id": 2001,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "Issue",
        "resource_id": 555,
        "label": {
            "id": 100,
            "name": "bug",
            "color": "#FF0000",
            "description": "Bug label"
        },
        "action": "add"
    }"##;

    let event: GitLabLabelEvent =
        serde_json::from_str(json).expect("Failed to deserialize label event");

    assert_eq!(event.id, 2001);
    assert_eq!(event.action, "add");
    let label = event.label.expect("label should be Some");
    assert_eq!(label.id, 100);
    assert_eq!(label.name, "bug");
    assert_eq!(label.color, Some("#FF0000".to_string()));
    assert_eq!(label.description, Some("Bug label".to_string()));
}

#[test]
fn deserializes_label_event_remove_null_color() {
    let json = r#"{
        "id": 2002,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "MergeRequest",
        "resource_id": 777,
        "label": {
            "id": 101,
            "name": "needs-review",
            "color": null,
            "description": null
        },
        "action": "remove"
    }"#;

    let event: GitLabLabelEvent =
        serde_json::from_str(json).expect("Failed to deserialize label remove event");

    assert_eq!(event.action, "remove");
    let label = event.label.expect("label should be Some");
    assert!(label.color.is_none());
    assert!(label.description.is_none());
}

#[test]
fn deserializes_label_event_null_label() {
    let json = r#"{
        "id": 2003,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "Issue",
        "resource_id": 999,
        "label": null,
        "action": "remove"
    }"#;

    let event: GitLabLabelEvent =
        serde_json::from_str(json).expect("Failed to deserialize label event with null label");

    assert_eq!(event.id, 2003);
    assert_eq!(event.action, "remove");
    assert!(event.label.is_none());
}

#[test]
fn deserializes_milestone_event_null_milestone() {
    let json = r#"{
        "id": 3002,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "Issue",
        "resource_id": 999,
        "milestone": null,
        "action": "remove"
    }"#;

    let event: GitLabMilestoneEvent = serde_json::from_str(json)
        .expect("Failed to deserialize milestone event with null milestone");

    assert_eq!(event.id, 3002);
    assert_eq!(event.action, "remove");
    assert!(event.milestone.is_none());
}

#[test]
fn deserializes_milestone_event() {
    let json = r#"{
        "id": 3001,
        "user": {
            "id": 42,
            "username": "developer",
            "name": "Dev User"
        },
        "created_at": "2024-03-15T10:30:00.000Z",
        "resource_type": "Issue",
        "resource_id": 555,
        "milestone": {
            "id": 200,
            "iid": 5,
            "title": "v1.0"
        },
        "action": "add"
    }"#;

    let event: GitLabMilestoneEvent =
        serde_json::from_str(json).expect("Failed to deserialize milestone event");

    assert_eq!(event.id, 3001);
    assert_eq!(event.action, "add");
    let milestone = event.milestone.expect("milestone should be Some");
    assert_eq!(milestone.id, 200);
    assert_eq!(milestone.iid, 5);
    assert_eq!(milestone.title, "v1.0");
}

#[test]
fn deserializes_merge_request_ref() {
    let json = r#"{
        "iid": 42,
        "title": "Feature branch",
        "web_url": "https://gitlab.example.com/group/project/-/merge_requests/42"
    }"#;

    let mr_ref: GitLabMergeRequestRef =
        serde_json::from_str(json).expect("Failed to deserialize MR ref");

    assert_eq!(mr_ref.iid, 42);
    assert_eq!(mr_ref.title, Some("Feature branch".to_string()));
    assert_eq!(
        mr_ref.web_url,
        Some("https://gitlab.example.com/group/project/-/merge_requests/42".to_string())
    );
}

#[test]
fn deserializes_label_ref() {
    let json = r##"{
        "id": 100,
        "name": "bug",
        "color": "#FF0000",
        "description": "Bug label"
    }"##;

    let label_ref: GitLabLabelRef =
        serde_json::from_str(json).expect("Failed to deserialize label ref");

    assert_eq!(label_ref.id, 100);
    assert_eq!(label_ref.name, "bug");
    assert_eq!(label_ref.color, Some("#FF0000".to_string()));
}

#[test]
fn deserializes_milestone_ref() {
    let json = r#"{
        "id": 200,
        "iid": 5,
        "title": "v1.0"
    }"#;

    let ms_ref: GitLabMilestoneRef =
        serde_json::from_str(json).expect("Failed to deserialize milestone ref");

    assert_eq!(ms_ref.id, 200);
    assert_eq!(ms_ref.iid, 5);
    assert_eq!(ms_ref.title, "v1.0");
}

#[test]
fn deserializes_gitlab_issue_ref() {
    let json = r#"{
        "id": 5001,
        "iid": 42,
        "project_id": 100,
        "title": "Fix authentication bug",
        "state": "opened",
        "web_url": "https://gitlab.example.com/group/project/-/issues/42"
    }"#;

    let issue_ref: GitLabIssueRef =
        serde_json::from_str(json).expect("Failed to deserialize issue ref");

    assert_eq!(issue_ref.id, 5001);
    assert_eq!(issue_ref.iid, 42);
    assert_eq!(issue_ref.project_id, 100);
    assert_eq!(issue_ref.title, "Fix authentication bug");
    assert_eq!(issue_ref.state, "opened");
    assert_eq!(
        issue_ref.web_url,
        "https://gitlab.example.com/group/project/-/issues/42"
    );
}

#[test]
fn deserializes_gitlab_issue_ref_array() {
    let json = r#"[
        {
            "id": 5001,
            "iid": 42,
            "project_id": 100,
            "title": "Issue one",
            "state": "opened",
            "web_url": "https://gitlab.example.com/-/issues/42"
        },
        {
            "id": 5002,
            "iid": 43,
            "project_id": 200,
            "title": "Issue two from another project",
            "state": "closed",
            "web_url": "https://gitlab.example.com/-/issues/43"
        }
    ]"#;

    let refs: Vec<GitLabIssueRef> =
        serde_json::from_str(json).expect("Failed to deserialize issue ref array");

    assert_eq!(refs.len(), 2);
    assert_eq!(refs[0].iid, 42);
    assert_eq!(refs[0].project_id, 100);
    assert_eq!(refs[1].iid, 43);
    assert_eq!(refs[1].project_id, 200);
    assert_eq!(refs[1].state, "closed");
}
