//! Generate Copilot Studio agent configuration (topics, instructions) from quelch config.

use crate::config::{Config, SourceConfig};
use std::fmt::Write;

/// Generated output for a Copilot Studio agent.
pub struct CopilotOutput {
    /// Agent instructions (system prompt) as markdown text.
    pub instructions: String,
    /// OnKnowledgeRequested YAML topics, one per index.
    pub topics: Vec<GeneratedTopic>,
    /// Human-readable guide explaining how to use the generated files.
    pub guide: String,
}

/// A generated Copilot Studio topic.
pub struct GeneratedTopic {
    /// Suggested filename (e.g., "jira-search.mcs.yaml").
    pub filename: String,
    /// The YAML content.
    pub yaml: String,
}

/// Generate Copilot Studio agent configuration from a quelch config.
pub fn generate(config: &Config) -> CopilotOutput {
    let mut topics = Vec::new();

    for source in &config.sources {
        let index_name = source.index();
        let endpoint = &config.azure.endpoint;
        let semantic_config = format!("{index_name}-semantic-config");

        match source {
            SourceConfig::Jira(jira) => {
                topics.push(GeneratedTopic {
                    filename: format!("{}-search.mcs.yaml", jira.name),
                    yaml: jira_topic(endpoint, index_name, &semantic_config),
                });
            }
            SourceConfig::Confluence(conf) => {
                topics.push(GeneratedTopic {
                    filename: format!("{}-search.mcs.yaml", conf.name),
                    yaml: confluence_topic(endpoint, index_name, &semantic_config),
                });
            }
        }
    }

    let instructions = agent_instructions(config);
    let guide = usage_guide(config, &topics);

    CopilotOutput {
        instructions,
        topics,
        guide,
    }
}

/// Generate the OnKnowledgeRequested YAML topic for a Jira index.
fn jira_topic(endpoint: &str, index_name: &str, semantic_config: &str) -> String {
    format!(
        r#"# OnKnowledgeRequested topic for Jira issues index "{index_name}"
# Generated by quelch — paste into Copilot Studio code editor
#
# This topic fires when the agent needs to search for knowledge.
# It queries Azure AI Search with semantic search + the full set of
# filterable/facetable fields that quelch indexes.

kind: AdaptiveDialog
beginDialog:
  kind: OnKnowledgeRequested
  id: main
  intent: {{}}
  actions:
    - kind: HttpRequestAction
      id: jiraSearch
      method: POST
      url: "{endpoint}/indexes('{index_name}')/docs/search.post.search?api-version=2024-07-01"
      headers:
        Content-Type: application/json
        api-key: "{{{{System.Env.AZURE_SEARCH_API_KEY}}}}"
      body:
        kind: Json
        value:
          search: =System.SearchQuery
          queryType: semantic
          semanticConfiguration: "{semantic_config}"
          select: "summary,content,url,assignee,project,status,priority,issue_type,issue_key,labels,created_at,updated_at"
          top: 15
          searchMode: any
          count: true
          captions: extractive
          answers: extractive
      response: Topic.searchResults
      responseSchema:
        kind: Record
        properties:
          "@@odata.count":
            type: Number
          value:
            type:
              kind: Table
              properties:
                summary: String
                content: String
                url: String
                assignee: String
                project: String
                status: String
                priority: String
                issue_type: String
                issue_key: String
                labels: String
                created_at: String
                updated_at: String

    - kind: SetVariable
      id: setSearchResults
      variable: System.SearchResults
      value: |-
        =ForAll(Topic.searchResults.value,
        {{
          Content: issue_key & " [" & status & "] " & summary & " (assigned to: " & assignee & ", project: " & project & ", priority: " & priority & ")" & Char(10) & content,
          ContentLocation: url,
          Title: issue_key & " — " & summary
        }})

inputType: {{}}
outputType: {{}}
"#
    )
}

/// Generate the OnKnowledgeRequested YAML topic for a Confluence index.
fn confluence_topic(endpoint: &str, index_name: &str, semantic_config: &str) -> String {
    format!(
        r#"# OnKnowledgeRequested topic for Confluence pages index "{index_name}"
# Generated by quelch — paste into Copilot Studio code editor
#
# This topic fires when the agent needs to search for knowledge.
# It queries Azure AI Search with semantic search over chunked
# Confluence page content.

kind: AdaptiveDialog
beginDialog:
  kind: OnKnowledgeRequested
  id: main
  intent: {{}}
  actions:
    - kind: HttpRequestAction
      id: confluenceSearch
      method: POST
      url: "{endpoint}/indexes('{index_name}')/docs/search.post.search?api-version=2024-07-01"
      headers:
        Content-Type: application/json
        api-key: "{{{{System.Env.AZURE_SEARCH_API_KEY}}}}"
      body:
        kind: Json
        value:
          search: =System.SearchQuery
          queryType: semantic
          semanticConfiguration: "{semantic_config}"
          select: "page_title,content,url,space_key,author,chunk_heading,labels,created_at,updated_at"
          top: 15
          searchMode: any
          captions: extractive
          answers: extractive
      response: Topic.searchResults
      responseSchema:
        kind: Record
        properties:
          value:
            type:
              kind: Table
              properties:
                page_title: String
                content: String
                url: String
                space_key: String
                author: String
                chunk_heading: String
                labels: String
                created_at: String
                updated_at: String

    - kind: SetVariable
      id: setSearchResults
      variable: System.SearchResults
      value: |-
        =ForAll(Topic.searchResults.value,
        {{
          Content: "[" & space_key & "] " & page_title & If(!IsBlank(chunk_heading), " > " & chunk_heading, "") & Char(10) & content,
          ContentLocation: url,
          Title: page_title & If(!IsBlank(chunk_heading), " — " & chunk_heading, "")
        }})

inputType: {{}}
outputType: {{}}
"#
    )
}

/// Generate agent instructions (system prompt) tailored to the configured sources.
fn agent_instructions(config: &Config) -> String {
    let mut out = String::new();

    writeln!(out, "# Agent Instructions").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "You are a knowledge assistant with access to data from the following sources:"
    )
    .unwrap();
    writeln!(out).unwrap();

    let jira_sources: Vec<_> = config
        .sources
        .iter()
        .filter_map(|s| match s {
            SourceConfig::Jira(j) => Some(j),
            _ => None,
        })
        .collect();

    let confluence_sources: Vec<_> = config
        .sources
        .iter()
        .filter_map(|s| match s {
            SourceConfig::Confluence(c) => Some(c),
            _ => None,
        })
        .collect();

    if !jira_sources.is_empty() {
        writeln!(out, "## Jira Issues").unwrap();
        writeln!(out).unwrap();
        for j in &jira_sources {
            let projects = j.projects.join(", ");
            writeln!(out, "- **{}**: Issues from projects: {}", j.name, projects).unwrap();
        }
        writeln!(out).unwrap();
        writeln!(
            out,
            "Each Jira issue has the following fields that you can reference in your answers:"
        )
        .unwrap();
        writeln!(out).unwrap();
        writeln!(out, "| Field | Description | Example |").unwrap();
        writeln!(out, "|-------|-------------|---------|").unwrap();
        writeln!(out, "| issue_key | The Jira issue identifier | PROJ-123 |").unwrap();
        writeln!(
            out,
            "| summary | The issue title/summary | Fix login button on mobile |"
        )
        .unwrap();
        writeln!(
            out,
            "| description | Detailed issue description | Full text of the issue body |"
        )
        .unwrap();
        writeln!(out, "| status | Current status | Open, In Progress, Done |").unwrap();
        writeln!(
            out,
            "| status_category | Status category | To Do, In Progress, Done |"
        )
        .unwrap();
        writeln!(
            out,
            "| priority | Issue priority | Critical, High, Medium, Low |"
        )
        .unwrap();
        writeln!(
            out,
            "| issue_type | Type of issue | Bug, Story, Task, Epic |"
        )
        .unwrap();
        writeln!(
            out,
            "| assignee | Person assigned to the issue | John Doe |"
        )
        .unwrap();
        writeln!(
            out,
            "| reporter | Person who created the issue | Jane Smith |"
        )
        .unwrap();
        writeln!(out, "| project | Project key | PROJ |").unwrap();
        writeln!(
            out,
            "| labels | Tags/labels on the issue | backend, urgent |"
        )
        .unwrap();
        writeln!(
            out,
            "| comments | Discussion comments on the issue | Free text |"
        )
        .unwrap();
        writeln!(out, "| created_at | When the issue was created | Date |").unwrap();
        writeln!(
            out,
            "| updated_at | When the issue was last updated | Date |"
        )
        .unwrap();
        writeln!(out, "| url | Link to the issue in Jira | URL |").unwrap();
        writeln!(out).unwrap();
    }

    if !confluence_sources.is_empty() {
        writeln!(out, "## Confluence Pages").unwrap();
        writeln!(out).unwrap();
        for c in &confluence_sources {
            let spaces = c.spaces.join(", ");
            writeln!(out, "- **{}**: Pages from spaces: {}", c.name, spaces).unwrap();
        }
        writeln!(out).unwrap();
        writeln!(
            out,
            "Each Confluence page is split into chunks by heading. Fields available:"
        )
        .unwrap();
        writeln!(out).unwrap();
        writeln!(out, "| Field | Description |").unwrap();
        writeln!(out, "|-------|-------------|").unwrap();
        writeln!(out, "| page_title | The page title |").unwrap();
        writeln!(out, "| chunk_heading | Section heading within the page |").unwrap();
        writeln!(out, "| body | The text content of this section |").unwrap();
        writeln!(out, "| space_key | Confluence space key |").unwrap();
        writeln!(out, "| author | Page author |").unwrap();
        writeln!(out, "| labels | Page labels/tags |").unwrap();
        writeln!(out, "| url | Link to the page in Confluence |").unwrap();
        writeln!(out).unwrap();
    }

    writeln!(out, "## How to Answer Questions").unwrap();
    writeln!(out).unwrap();

    if !jira_sources.is_empty() {
        writeln!(out, "### Jira Questions").unwrap();
        writeln!(out).unwrap();
        writeln!(out, "When answering questions about Jira issues:").unwrap();
        writeln!(out).unwrap();
        writeln!(out, "- **Always include the issue key** (e.g., PROJ-123) and a link to the issue when referencing specific issues.").unwrap();
        writeln!(out, "- **Include relevant metadata** such as status, assignee, and priority when listing issues.").unwrap();
        writeln!(out, "- When asked about issues assigned to a person, search for their name and present matching results.").unwrap();
        writeln!(out, "- When asked to count or list \"all\" issues, be transparent that you can only search and return the most relevant results from the index — you may not have visibility into every issue. State the number of results you found and note that there may be more.").unwrap();
        writeln!(out, "- When asked about a specific project, mention the project key in your search to improve relevance.").unwrap();
        writeln!(
            out,
            "- Format issue lists as tables when presenting multiple issues."
        )
        .unwrap();
        writeln!(out).unwrap();
    }

    if !confluence_sources.is_empty() {
        writeln!(out, "### Confluence Questions").unwrap();
        writeln!(out).unwrap();
        writeln!(out, "When answering questions about Confluence content:").unwrap();
        writeln!(out).unwrap();
        writeln!(
            out,
            "- **Always link to the source page** so the user can read the full context."
        )
        .unwrap();
        writeln!(
            out,
            "- Reference the page title and section heading when quoting content."
        )
        .unwrap();
        writeln!(out, "- If multiple chunks from the same page are relevant, synthesize them into a coherent answer rather than repeating page references.").unwrap();
        writeln!(out).unwrap();
    }

    writeln!(out, "### General Guidelines").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "- If you cannot find relevant information, say so clearly rather than guessing."
    )
    .unwrap();
    writeln!(out, "- Always cite your sources with links.").unwrap();
    writeln!(
        out,
        "- Be concise but complete. Prefer tables for structured data and prose for explanations."
    )
    .unwrap();

    out
}

/// Generate a usage guide explaining what was generated and how to use it.
fn usage_guide(config: &Config, topics: &[GeneratedTopic]) -> String {
    let mut out = String::new();

    writeln!(out, "# Copilot Studio Agent Setup Guide").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "Generated by `quelch generate-agent` based on your quelch.yaml configuration."
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "## What Was Generated").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "| File | Purpose |").unwrap();
    writeln!(out, "|------|---------|").unwrap();
    writeln!(
        out,
        "| `agent-instructions.md` | System prompt / agent instructions — paste into your agent's Instructions field |"
    )
    .unwrap();
    for topic in topics {
        writeln!(
            out,
            "| `{}` | OnKnowledgeRequested topic — custom search logic for this index |",
            topic.filename
        )
        .unwrap();
    }
    writeln!(out, "| `guide.md` | This file — setup instructions |").unwrap();
    writeln!(out).unwrap();

    writeln!(out, "## Setup Steps").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "### 1. Create or Open Your Agent in Copilot Studio").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "Go to [Copilot Studio](https://copilotstudio.microsoft.com) and create a new agent or open an existing one."
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "### 2. Set the Agent Instructions").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "Copy the contents of `agent-instructions.md` into your agent's **Instructions** field."
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "This tells the agent what data it has access to and how to format answers."
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "### 3. Add the OnKnowledgeRequested Topics").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "For each generated `.mcs.yaml` file:").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "1. In Copilot Studio, go to **Topics**").unwrap();
    writeln!(out, "2. Click **Add** > **Topic** > **From blank**").unwrap();
    writeln!(
        out,
        "3. Click the ellipsis (**...**) in the top-right and select **Open code editor**"
    )
    .unwrap();
    writeln!(
        out,
        "4. Replace the default YAML with the contents of the generated file"
    )
    .unwrap();
    writeln!(out, "5. Save the topic").unwrap();
    writeln!(out).unwrap();

    writeln!(out, "### 4. Configure Authentication").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "The generated topics reference `{{{{System.Env.AZURE_SEARCH_API_KEY}}}}` for the API key."
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "You have two options to provide the API key:").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "**Option A: Environment variable** — Set `AZURE_SEARCH_API_KEY` as an environment variable in your Copilot Studio environment."
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "**Option B: Hardcode the key** — Replace `{{{{System.Env.AZURE_SEARCH_API_KEY}}}}` in the YAML with your actual Azure AI Search query key. Less secure but simpler for testing."
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "Use a **query key** (read-only), not your admin key.").unwrap();
    writeln!(out).unwrap();

    writeln!(
        out,
        "### 5. Remove Built-in Azure AI Search Knowledge Source"
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "If you previously added your Azure AI Search index as a built-in knowledge source (via the Knowledge page), **remove it**. The OnKnowledgeRequested topics replace the built-in integration with full query control. Having both would cause duplicate or conflicting results."
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "### 6. Publish and Test").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "Publish your agent and test with queries like:").unwrap();
    writeln!(out).unwrap();

    let has_jira = config
        .sources
        .iter()
        .any(|s| matches!(s, SourceConfig::Jira(_)));
    let has_confluence = config
        .sources
        .iter()
        .any(|s| matches!(s, SourceConfig::Confluence(_)));

    if has_jira {
        writeln!(out, "- \"Find Jira issues about wifi problems\"").unwrap();
        writeln!(out, "- \"Show me issues assigned to John Doe\"").unwrap();
        writeln!(
            out,
            "- \"What high-priority bugs are open in the PROJ project?\""
        )
        .unwrap();
        writeln!(out, "- \"Summarize recent activity in the ENG project\"").unwrap();
    }
    if has_confluence {
        writeln!(out, "- \"How do I set up the development environment?\"").unwrap();
        writeln!(out, "- \"What does our deployment process look like?\"").unwrap();
        writeln!(out, "- \"Find documentation about authentication\"").unwrap();
    }
    writeln!(out).unwrap();

    writeln!(out, "## How It Works").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "The `OnKnowledgeRequested` trigger fires when the agent's LLM decides it needs to search for information. The topic then:").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "1. Takes `System.SearchQuery` (a context-aware rewrite of the user's question)"
    )
    .unwrap();
    writeln!(
        out,
        "2. Sends it to Azure AI Search using semantic search with your configured index"
    )
    .unwrap();
    writeln!(
        out,
        "3. Returns up to 15 results with structured metadata (issue key, status, assignee, etc.)"
    )
    .unwrap();
    writeln!(
        out,
        "4. The agent's LLM uses these results to generate a grounded answer with citations"
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "The key advantage over the built-in Azure AI Search knowledge source is that the `Content` field in the results includes structured metadata (assignee, status, project, priority), which helps the LLM answer questions like \"who is assigned to this?\" or \"what's the status?\" even though it's using semantic search under the hood.").unwrap();
    writeln!(out).unwrap();

    writeln!(out, "## Customization").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "### Adding OData Filters").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "You can add a `filter` field to the search body to scope results. For example:"
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "```yaml").unwrap();
    writeln!(out, "body:").unwrap();
    writeln!(out, "  kind: Json").unwrap();
    writeln!(out, "  value:").unwrap();
    writeln!(out, "    search: =System.SearchQuery").unwrap();
    writeln!(
        out,
        "    filter: \"project eq 'PROJ' and status_category ne 'Done'\""
    )
    .unwrap();
    writeln!(out, "    # ... rest of search parameters").unwrap();
    writeln!(out, "```").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "Available filter fields for Jira:").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "| Field | Type | Example Filter |").unwrap();
    writeln!(out, "|-------|------|---------------|").unwrap();
    writeln!(out, "| assignee | string | `assignee eq 'John Doe'` |").unwrap();
    writeln!(out, "| project | string | `project eq 'PROJ'` |").unwrap();
    writeln!(out, "| status | string | `status eq 'In Progress'` |").unwrap();
    writeln!(
        out,
        "| status_category | string | `status_category eq 'Done'` |"
    )
    .unwrap();
    writeln!(out, "| priority | string | `priority eq 'High'` |").unwrap();
    writeln!(out, "| issue_type | string | `issue_type eq 'Bug'` |").unwrap();
    writeln!(
        out,
        "| labels | string collection | `labels/any(l: l eq 'urgent')` |"
    )
    .unwrap();
    writeln!(
        out,
        "| created_at | datetime | `created_at ge 2024-01-01T00:00:00Z` |"
    )
    .unwrap();
    writeln!(
        out,
        "| updated_at | datetime | `updated_at ge 2024-01-01T00:00:00Z` |"
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "### Changing Result Count").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "The `top` parameter controls how many results are returned. The maximum useful value is **15** — Copilot Studio uses at most 15 snippets for answer generation."
    )
    .unwrap();
    writeln!(out).unwrap();

    writeln!(out, "## References").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "- [Copilot Studio: Custom Knowledge Sources](https://learn.microsoft.com/en-us/microsoft-copilot-studio/guidance/custom-knowledge-sources)").unwrap();
    writeln!(out, "- [Azure AI Search POST Search API](https://learn.microsoft.com/en-us/rest/api/searchservice/documents/search-post)").unwrap();
    writeln!(out, "- [OData Filter Syntax](https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter)").unwrap();
    writeln!(out, "- [Copilot Studio YAML Code Editor](https://learn.microsoft.com/en-us/microsoft-copilot-studio/guidance/topics-code-editor)").unwrap();

    out
}

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

    fn test_config() -> Config {
        Config {
            azure: AzureConfig {
                endpoint: "https://my-search.search.windows.net".to_string(),
                api_key: "test-key".to_string(),
            },
            sources: vec![
                SourceConfig::Jira(JiraSourceConfig {
                    name: "my-jira".to_string(),
                    url: "https://company.atlassian.net".to_string(),
                    auth: AuthConfig::Cloud {
                        email: "user@test.com".to_string(),
                        api_token: "token".to_string(),
                    },
                    projects: vec!["PROJ".to_string(), "ENG".to_string()],
                    index: "jira-issues".to_string(),
                }),
                SourceConfig::Confluence(ConfluenceSourceConfig {
                    name: "my-confluence".to_string(),
                    url: "https://company.atlassian.net/wiki".to_string(),
                    auth: AuthConfig::Cloud {
                        email: "user@test.com".to_string(),
                        api_token: "token".to_string(),
                    },
                    spaces: vec!["ENG".to_string(), "DOCS".to_string()],
                    index: "confluence-pages".to_string(),
                }),
            ],
            sync: SyncConfig::default(),
        }
    }

    #[test]
    fn generates_topics_for_each_source() {
        let output = generate(&test_config());
        assert_eq!(output.topics.len(), 2);
        assert_eq!(output.topics[0].filename, "my-jira-search.mcs.yaml");
        assert_eq!(output.topics[1].filename, "my-confluence-search.mcs.yaml");
    }

    #[test]
    fn jira_topic_contains_endpoint_and_index() {
        let output = generate(&test_config());
        let yaml = &output.topics[0].yaml;
        assert!(yaml.contains("my-search.search.windows.net"));
        assert!(yaml.contains("jira-issues"));
        assert!(yaml.contains("jira-issues-semantic-config"));
    }

    #[test]
    fn confluence_topic_contains_endpoint_and_index() {
        let output = generate(&test_config());
        let yaml = &output.topics[1].yaml;
        assert!(yaml.contains("my-search.search.windows.net"));
        assert!(yaml.contains("confluence-pages"));
        assert!(yaml.contains("confluence-pages-semantic-config"));
    }

    #[test]
    fn instructions_mention_configured_projects() {
        let output = generate(&test_config());
        assert!(output.instructions.contains("PROJ"));
        assert!(output.instructions.contains("ENG"));
    }

    #[test]
    fn instructions_mention_configured_spaces() {
        let output = generate(&test_config());
        assert!(output.instructions.contains("ENG"));
        assert!(output.instructions.contains("DOCS"));
    }

    #[test]
    fn guide_lists_generated_files() {
        let output = generate(&test_config());
        assert!(output.guide.contains("my-jira-search.mcs.yaml"));
        assert!(output.guide.contains("my-confluence-search.mcs.yaml"));
        assert!(output.guide.contains("agent-instructions.md"));
    }

    #[test]
    fn jira_only_config() {
        let config = Config {
            azure: AzureConfig {
                endpoint: "https://test.search.windows.net".to_string(),
                api_key: "key".to_string(),
            },
            sources: vec![SourceConfig::Jira(JiraSourceConfig {
                name: "jira".to_string(),
                url: "https://jira.example.com".to_string(),
                auth: AuthConfig::DataCenter {
                    pat: "pat".to_string(),
                },
                projects: vec!["HR".to_string()],
                index: "jira-idx".to_string(),
            })],
            sync: SyncConfig::default(),
        };
        let output = generate(&config);
        assert_eq!(output.topics.len(), 1);
        assert!(output.instructions.contains("Jira Issues"));
        assert!(!output.instructions.contains("Confluence Pages"));
    }
}
