#![allow(dead_code)]

use anyhow::{Context, Result};
use rusqlite::Connection;

use crate::message::EntityKey;
use crate::state::mr_detail::{FileChange, FileChangeType, MrDetailData, MrMetadata};
use crate::view::common::cross_ref::{CrossRef, CrossRefKind};
use crate::view::common::discussion_tree::{DiscussionNode, NoteNode};

/// Fetch MR metadata + cross-refs + file changes (Phase 1 composite).
pub fn fetch_mr_detail(conn: &Connection, key: &EntityKey) -> Result<MrDetailData> {
    let metadata = fetch_mr_metadata(conn, key)?;
    let cross_refs = fetch_mr_cross_refs(conn, key)?;
    let file_changes = fetch_mr_file_changes(conn, key)?;
    Ok(MrDetailData {
        metadata,
        cross_refs,
        file_changes,
    })
}

/// Fetch MR metadata from the local DB.
fn fetch_mr_metadata(conn: &Connection, key: &EntityKey) -> Result<MrMetadata> {
    let row = conn
        .query_row(
            "SELECT m.iid, p.path_with_namespace, m.title,
                 COALESCE(m.description, ''), m.state, m.draft,
                 m.author_username, m.source_branch, m.target_branch,
                 COALESCE(m.detailed_merge_status, ''),
                 m.created_at, m.updated_at, m.merged_at,
                 COALESCE(m.web_url, ''),
                 (SELECT COUNT(*) FROM discussions d WHERE d.merge_request_id = m.id) AS disc_count,
                 (SELECT COUNT(*) FROM mr_file_changes fc WHERE fc.merge_request_id = m.id) AS fc_count
             FROM merge_requests m
             JOIN projects p ON p.id = m.project_id
             WHERE m.project_id = ?1 AND m.iid = ?2",
            rusqlite::params![key.project_id, key.iid],
            |row| {
                Ok(MrMetadata {
                    iid: row.get(0)?,
                    project_path: row.get(1)?,
                    title: row.get::<_, Option<String>>(2)?.unwrap_or_default(),
                    description: row.get(3)?,
                    state: row.get::<_, Option<String>>(4)?.unwrap_or_default(),
                    draft: row.get(5)?,
                    author: row.get::<_, Option<String>>(6)?.unwrap_or_default(),
                    assignees: Vec::new(),
                    reviewers: Vec::new(),
                    labels: Vec::new(),
                    source_branch: row.get::<_, Option<String>>(7)?.unwrap_or_default(),
                    target_branch: row.get::<_, Option<String>>(8)?.unwrap_or_default(),
                    merge_status: row.get(9)?,
                    created_at: row.get(10)?,
                    updated_at: row.get(11)?,
                    merged_at: row.get(12)?,
                    web_url: row.get(13)?,
                    discussion_count: row.get::<_, i64>(14)? as usize,
                    file_change_count: row.get::<_, i64>(15)? as usize,
                })
            },
        )
        .context("fetching MR metadata")?;

    // Fetch assignees.
    let mut assignees_stmt = conn
        .prepare(
            "SELECT username FROM mr_assignees
             WHERE merge_request_id = (
                 SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2
             )
             ORDER BY username",
        )
        .context("preparing assignees query")?;
    let assignees: Vec<String> = assignees_stmt
        .query_map(rusqlite::params![key.project_id, key.iid], |row| row.get(0))
        .context("fetching assignees")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading assignee row")?;

    // Fetch reviewers.
    let mut reviewers_stmt = conn
        .prepare(
            "SELECT username FROM mr_reviewers
             WHERE merge_request_id = (
                 SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2
             )
             ORDER BY username",
        )
        .context("preparing reviewers query")?;
    let reviewers: Vec<String> = reviewers_stmt
        .query_map(rusqlite::params![key.project_id, key.iid], |row| row.get(0))
        .context("fetching reviewers")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading reviewer row")?;

    // Fetch labels.
    let mut labels_stmt = conn
        .prepare(
            "SELECT l.name FROM mr_labels ml
             JOIN labels l ON ml.label_id = l.id
             WHERE ml.merge_request_id = (
                 SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2
             )
             ORDER BY l.name",
        )
        .context("preparing labels query")?;
    let labels: Vec<String> = labels_stmt
        .query_map(rusqlite::params![key.project_id, key.iid], |row| row.get(0))
        .context("fetching labels")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading label row")?;

    let mut result = row;
    result.assignees = assignees;
    result.reviewers = reviewers;
    result.labels = labels;
    Ok(result)
}

/// Fetch cross-references for an MR.
fn fetch_mr_cross_refs(conn: &Connection, key: &EntityKey) -> Result<Vec<CrossRef>> {
    let mut stmt = conn
        .prepare(
            "SELECT er.reference_type, er.target_entity_type,
                    er.target_entity_id, er.target_entity_iid,
                    er.target_project_path,
                    CASE
                        WHEN er.target_entity_type = 'issue'
                            THEN (SELECT title FROM issues WHERE id = er.target_entity_id)
                        WHEN er.target_entity_type = 'merge_request'
                            THEN (SELECT title FROM merge_requests WHERE id = er.target_entity_id)
                        ELSE NULL
                    END as entity_title,
                    CASE
                        WHEN er.target_entity_id IS NOT NULL
                            THEN (SELECT project_id FROM issues WHERE id = er.target_entity_id
                                  UNION ALL
                                  SELECT project_id FROM merge_requests WHERE id = er.target_entity_id
                                  LIMIT 1)
                        ELSE NULL
                    END as target_project_id
             FROM entity_references er
             WHERE er.source_entity_type = 'merge_request'
               AND er.source_entity_id = (SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2)
             ORDER BY er.reference_type, er.target_entity_iid",
        )
        .context("preparing MR cross-refs query")?;

    let refs: Vec<CrossRef> = stmt
        .query_map(rusqlite::params![key.project_id, key.iid], |row| {
            let ref_type: String = row.get(0)?;
            let target_type: String = row.get(1)?;
            let _target_id: Option<i64> = row.get(2)?;
            let target_iid: Option<i64> = row.get(3)?;
            let target_path: Option<String> = row.get(4)?;
            let title: Option<String> = row.get(5)?;
            let target_project_id: Option<i64> = row.get(6)?;

            let kind = match (ref_type.as_str(), target_type.as_str()) {
                ("closes", "issue") => CrossRefKind::ClosingMr,
                ("related", "issue") => CrossRefKind::RelatedIssue,
                _ => CrossRefKind::MentionedIn,
            };

            let iid = target_iid.unwrap_or(0);
            let project_id = target_project_id.unwrap_or(key.project_id);

            let entity_key = match target_type.as_str() {
                "merge_request" => EntityKey::mr(project_id, iid),
                _ => EntityKey::issue(project_id, iid),
            };

            let label = title.unwrap_or_else(|| {
                let prefix = if target_type == "merge_request" {
                    "!"
                } else {
                    "#"
                };
                let path = target_path.clone().unwrap_or_default();
                if path.is_empty() {
                    format!("{prefix}{iid}")
                } else {
                    format!("{path}{prefix}{iid}")
                }
            });

            Ok(CrossRef {
                kind,
                entity_key,
                label,
                navigable: target_project_id.is_some(),
            })
        })
        .context("fetching MR cross-refs")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading cross-ref row")?;

    Ok(refs)
}

/// Fetch file changes for an MR.
fn fetch_mr_file_changes(conn: &Connection, key: &EntityKey) -> Result<Vec<FileChange>> {
    let mut stmt = conn
        .prepare(
            "SELECT fc.old_path, fc.new_path, fc.change_type
             FROM mr_file_changes fc
             WHERE fc.merge_request_id = (
                 SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2
             )
             ORDER BY fc.new_path",
        )
        .context("preparing file changes query")?;

    let changes: Vec<FileChange> = stmt
        .query_map(rusqlite::params![key.project_id, key.iid], |row| {
            Ok(FileChange {
                old_path: row.get(0)?,
                new_path: row.get(1)?,
                change_type: FileChangeType::parse_db(&row.get::<_, String>(2).unwrap_or_default()),
            })
        })
        .context("fetching file changes")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading file change row")?;

    Ok(changes)
}

/// Fetch discussions for an MR (Phase 2 async load).
pub fn fetch_mr_discussions(conn: &Connection, key: &EntityKey) -> Result<Vec<DiscussionNode>> {
    let mr_id: i64 = conn
        .query_row(
            "SELECT id FROM merge_requests WHERE project_id = ?1 AND iid = ?2",
            rusqlite::params![key.project_id, key.iid],
            |row| row.get(0),
        )
        .context("looking up MR id for discussions")?;

    let mut disc_stmt = conn
        .prepare(
            "SELECT d.id, d.gitlab_discussion_id, d.resolvable, d.resolved
             FROM discussions d
             WHERE d.merge_request_id = ?1
             ORDER BY d.first_note_at ASC",
        )
        .context("preparing MR discussions query")?;

    let mut note_stmt = conn
        .prepare(
            "SELECT n.author_username, n.body, n.created_at, n.is_system,
                    n.note_type, n.position_new_path, n.position_new_line
             FROM notes n
             WHERE n.discussion_id = ?1
             ORDER BY n.position ASC, n.created_at ASC",
        )
        .context("preparing MR notes query")?;

    let disc_rows: Vec<_> = disc_stmt
        .query_map(rusqlite::params![mr_id], |row| {
            Ok((
                row.get::<_, i64>(0)?,    // id
                row.get::<_, String>(1)?, // gitlab_discussion_id
                row.get::<_, bool>(2)?,   // resolvable
                row.get::<_, bool>(3)?,   // resolved
            ))
        })
        .context("fetching MR discussions")?
        .collect::<std::result::Result<Vec<_>, _>>()
        .context("reading discussion row")?;

    let mut discussions = Vec::new();
    for (disc_db_id, discussion_id, resolvable, resolved) in disc_rows {
        let notes: Vec<NoteNode> = note_stmt
            .query_map(rusqlite::params![disc_db_id], |row| {
                Ok(NoteNode {
                    author: row.get::<_, Option<String>>(0)?.unwrap_or_default(),
                    body: row.get::<_, Option<String>>(1)?.unwrap_or_default(),
                    created_at: row.get(2)?,
                    is_system: row.get(3)?,
                    is_diff_note: row.get::<_, Option<String>>(4)?.as_deref() == Some("DiffNote"),
                    diff_file_path: row.get(5)?,
                    diff_new_line: row.get(6)?,
                })
            })
            .context("fetching notes")?
            .collect::<std::result::Result<Vec<_>, _>>()
            .context("reading note row")?;

        discussions.push(DiscussionNode {
            discussion_id,
            notes,
            resolvable,
            resolved,
        });
    }

    Ok(discussions)
}

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

    fn create_issue_detail_schema(conn: &Connection) {
        conn.execute_batch(
            "
            CREATE TABLE projects (
                id INTEGER PRIMARY KEY,
                gitlab_project_id INTEGER UNIQUE NOT NULL,
                path_with_namespace TEXT NOT NULL
            );
            CREATE TABLE issues (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                project_id INTEGER NOT NULL,
                iid INTEGER NOT NULL,
                title TEXT NOT NULL,
                description TEXT,
                state TEXT NOT NULL DEFAULT 'opened',
                author_username TEXT,
                milestone_title TEXT,
                due_date TEXT,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                web_url TEXT,
                UNIQUE(project_id, iid)
            );
            CREATE TABLE issue_assignees (
                issue_id INTEGER NOT NULL,
                username TEXT NOT NULL,
                UNIQUE(issue_id, username)
            );
            CREATE TABLE labels (
                id INTEGER PRIMARY KEY,
                project_id INTEGER NOT NULL,
                name TEXT NOT NULL
            );
            CREATE TABLE issue_labels (
                issue_id INTEGER NOT NULL,
                label_id INTEGER NOT NULL,
                UNIQUE(issue_id, label_id)
            );
            CREATE TABLE discussions (
                id INTEGER PRIMARY KEY,
                gitlab_discussion_id TEXT NOT NULL,
                project_id INTEGER NOT NULL,
                issue_id INTEGER,
                merge_request_id INTEGER,
                noteable_type TEXT NOT NULL,
                resolvable INTEGER NOT NULL DEFAULT 0,
                resolved INTEGER NOT NULL DEFAULT 0,
                first_note_at INTEGER
            );
            CREATE TABLE notes (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                discussion_id INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                note_type TEXT,
                is_system INTEGER NOT NULL DEFAULT 0,
                author_username TEXT,
                body TEXT,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                position INTEGER,
                position_new_path TEXT,
                position_new_line INTEGER
            );
            CREATE TABLE entity_references (
                id INTEGER PRIMARY KEY,
                project_id INTEGER NOT NULL,
                source_entity_type TEXT NOT NULL,
                source_entity_id INTEGER NOT NULL,
                target_entity_type TEXT NOT NULL,
                target_entity_id INTEGER,
                target_project_path TEXT,
                target_entity_iid INTEGER,
                reference_type TEXT NOT NULL,
                source_method TEXT NOT NULL DEFAULT 'api',
                created_at INTEGER NOT NULL DEFAULT 0
            );
            CREATE TABLE merge_requests (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                project_id INTEGER NOT NULL,
                iid INTEGER NOT NULL,
                title TEXT NOT NULL,
                state TEXT NOT NULL DEFAULT 'opened',
                UNIQUE(project_id, iid)
            );
            ",
        )
        .unwrap();
    }

    fn create_mr_detail_schema(conn: &Connection) {
        create_issue_detail_schema(conn);
        // Add MR-specific columns and tables on top of the base schema.
        conn.execute_batch(
            "
            -- Add columns to merge_requests that the detail query needs.
            ALTER TABLE merge_requests ADD COLUMN description TEXT;
            ALTER TABLE merge_requests ADD COLUMN draft INTEGER NOT NULL DEFAULT 0;
            ALTER TABLE merge_requests ADD COLUMN author_username TEXT;
            ALTER TABLE merge_requests ADD COLUMN source_branch TEXT;
            ALTER TABLE merge_requests ADD COLUMN target_branch TEXT;
            ALTER TABLE merge_requests ADD COLUMN detailed_merge_status TEXT;
            ALTER TABLE merge_requests ADD COLUMN created_at INTEGER;
            ALTER TABLE merge_requests ADD COLUMN updated_at INTEGER;
            ALTER TABLE merge_requests ADD COLUMN merged_at INTEGER;
            ALTER TABLE merge_requests ADD COLUMN web_url TEXT;

            CREATE TABLE mr_assignees (
                merge_request_id INTEGER NOT NULL,
                username TEXT NOT NULL,
                UNIQUE(merge_request_id, username)
            );
            CREATE TABLE mr_reviewers (
                merge_request_id INTEGER NOT NULL,
                username TEXT NOT NULL,
                UNIQUE(merge_request_id, username)
            );
            CREATE TABLE mr_labels (
                merge_request_id INTEGER NOT NULL,
                label_id INTEGER NOT NULL,
                UNIQUE(merge_request_id, label_id)
            );
            CREATE TABLE mr_file_changes (
                id INTEGER PRIMARY KEY,
                merge_request_id INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                old_path TEXT,
                new_path TEXT NOT NULL,
                change_type TEXT NOT NULL
            );
            ",
        )
        .unwrap();
    }

    fn setup_mr_detail_data(conn: &Connection) {
        // Project (if not already inserted).
        conn.execute(
            "INSERT OR IGNORE INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
            [],
        )
        .unwrap();

        // MR.
        conn.execute(
            "INSERT INTO merge_requests (id, gitlab_id, project_id, iid, title, description, state, draft, author_username, source_branch, target_branch, detailed_merge_status, created_at, updated_at, merged_at, web_url)
             VALUES (1, 2000, 1, 10, 'Fix auth flow', 'MR description', 'opened', 0, 'alice', 'fix-auth', 'main', 'mergeable', 1700000000000, 1700000060000, NULL, 'https://gitlab.com/group/project/-/merge_requests/10')",
            [],
        )
        .unwrap();

        // Assignees.
        conn.execute(
            "INSERT INTO mr_assignees (merge_request_id, username) VALUES (1, 'bob')",
            [],
        )
        .unwrap();

        // Reviewers.
        conn.execute(
            "INSERT INTO mr_reviewers (merge_request_id, username) VALUES (1, 'carol')",
            [],
        )
        .unwrap();

        // Labels.
        conn.execute(
            "INSERT OR IGNORE INTO labels (id, project_id, name) VALUES (10, 1, 'backend')",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO mr_labels (merge_request_id, label_id) VALUES (1, 10)",
            [],
        )
        .unwrap();

        // File changes.
        conn.execute(
            "INSERT INTO mr_file_changes (merge_request_id, project_id, old_path, new_path, change_type)
             VALUES (1, 1, NULL, 'src/auth.rs', 'modified')",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO mr_file_changes (merge_request_id, project_id, old_path, new_path, change_type)
             VALUES (1, 1, NULL, 'src/lib.rs', 'added')",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO mr_file_changes (merge_request_id, project_id, old_path, new_path, change_type)
             VALUES (1, 1, 'src/old.rs', 'src/new.rs', 'renamed')",
            [],
        )
        .unwrap();

        // Discussion with a note.
        conn.execute(
            "INSERT INTO discussions (id, gitlab_discussion_id, project_id, merge_request_id, noteable_type, resolvable, resolved, first_note_at)
             VALUES (1, 'mr_disc_1', 1, 1, 'MergeRequest', 1, 0, 1700000010000)",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO notes (id, gitlab_id, discussion_id, project_id, note_type, is_system, author_username, body, created_at, updated_at, position, position_new_path, position_new_line)
             VALUES (1, 5001, 1, 1, 'DiffNote', 0, 'alice', 'Please fix this', 1700000010000, 1700000010000, 0, 'src/auth.rs', 42)",
            [],
        )
        .unwrap();

        // Cross-reference (MR closes issue).
        conn.execute(
            "INSERT INTO issues (id, gitlab_id, project_id, iid, title, state, created_at, updated_at)
             VALUES (1, 1000, 1, 5, 'Auth bug', 'opened', 0, 0)",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO entity_references (project_id, source_entity_type, source_entity_id, target_entity_type, target_entity_id, target_project_path, target_entity_iid, reference_type, source_method)
             VALUES (1, 'merge_request', 1, 'issue', 1, 'group/project', 5, 'closes', 'api')",
            [],
        )
        .unwrap();
    }

    #[test]
    fn test_fetch_mr_detail_basic_metadata() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);
        setup_mr_detail_data(&conn);

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();

        assert_eq!(data.metadata.iid, 10);
        assert_eq!(data.metadata.title, "Fix auth flow");
        assert_eq!(data.metadata.description, "MR description");
        assert_eq!(data.metadata.state, "opened");
        assert!(!data.metadata.draft);
        assert_eq!(data.metadata.author, "alice");
        assert_eq!(data.metadata.source_branch, "fix-auth");
        assert_eq!(data.metadata.target_branch, "main");
        assert_eq!(data.metadata.merge_status, "mergeable");
        assert!(data.metadata.merged_at.is_none());
        assert_eq!(
            data.metadata.web_url,
            "https://gitlab.com/group/project/-/merge_requests/10"
        );
    }

    #[test]
    fn test_fetch_mr_detail_assignees_reviewers_labels() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);
        setup_mr_detail_data(&conn);

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();

        assert_eq!(data.metadata.assignees, vec!["bob"]);
        assert_eq!(data.metadata.reviewers, vec!["carol"]);
        assert_eq!(data.metadata.labels, vec!["backend"]);
    }

    #[test]
    fn test_fetch_mr_detail_file_changes() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);
        setup_mr_detail_data(&conn);

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();

        assert_eq!(data.file_changes.len(), 3);
        assert_eq!(data.metadata.file_change_count, 3);

        // Ordered by new_path.
        assert_eq!(data.file_changes[0].new_path, "src/auth.rs");
        assert_eq!(data.file_changes[0].change_type, FileChangeType::Modified);

        assert_eq!(data.file_changes[1].new_path, "src/lib.rs");
        assert_eq!(data.file_changes[1].change_type, FileChangeType::Added);

        assert_eq!(data.file_changes[2].new_path, "src/new.rs");
        assert_eq!(data.file_changes[2].change_type, FileChangeType::Renamed);
        assert_eq!(data.file_changes[2].old_path.as_deref(), Some("src/old.rs"));
    }

    #[test]
    fn test_fetch_mr_detail_cross_refs() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);
        setup_mr_detail_data(&conn);

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();

        assert_eq!(data.cross_refs.len(), 1);
        assert_eq!(data.cross_refs[0].kind, CrossRefKind::ClosingMr);
        assert_eq!(data.cross_refs[0].label, "Auth bug");
    }

    #[test]
    fn test_fetch_mr_discussions() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);
        setup_mr_detail_data(&conn);

        let key = EntityKey::mr(1, 10);
        let discussions = fetch_mr_discussions(&conn, &key).unwrap();

        assert_eq!(discussions.len(), 1);
        assert_eq!(discussions[0].discussion_id, "mr_disc_1");
        assert!(discussions[0].resolvable);
        assert!(!discussions[0].resolved);
        assert_eq!(discussions[0].notes.len(), 1);
        assert_eq!(discussions[0].notes[0].author, "alice");
        assert_eq!(discussions[0].notes[0].body, "Please fix this");
        assert!(discussions[0].notes[0].is_diff_note);
        assert_eq!(
            discussions[0].notes[0].diff_file_path.as_deref(),
            Some("src/auth.rs")
        );
        assert_eq!(discussions[0].notes[0].diff_new_line, Some(42));
    }

    #[test]
    fn test_fetch_mr_detail_not_found() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);

        // Insert project but no MR.
        conn.execute(
            "INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'g/p')",
            [],
        )
        .unwrap();

        let key = EntityKey::mr(1, 99);
        assert!(fetch_mr_detail(&conn, &key).is_err());
    }

    #[test]
    fn test_fetch_mr_detail_no_file_changes() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);

        conn.execute(
            "INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'g/p')",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO merge_requests (id, gitlab_id, project_id, iid, title, state, created_at, updated_at, web_url)
             VALUES (1, 2000, 1, 10, 'Empty MR', 'opened', 0, 0, '')",
            [],
        )
        .unwrap();

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();
        assert!(data.file_changes.is_empty());
        assert_eq!(data.metadata.file_change_count, 0);
    }

    #[test]
    fn test_fetch_mr_detail_draft() {
        let conn = Connection::open_in_memory().unwrap();
        create_mr_detail_schema(&conn);

        conn.execute(
            "INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'g/p')",
            [],
        )
        .unwrap();
        conn.execute(
            "INSERT INTO merge_requests (id, gitlab_id, project_id, iid, title, state, draft, created_at, updated_at, web_url)
             VALUES (1, 2000, 1, 10, 'Draft: WIP', 'opened', 1, 0, 0, '')",
            [],
        )
        .unwrap();

        let key = EntityKey::mr(1, 10);
        let data = fetch_mr_detail(&conn, &key).unwrap();
        assert!(data.metadata.draft);
    }
}
