//! Ticket assistant infrastructure adapters.

use std::sync::Arc;

use async_trait::async_trait;
use domain::{
    AssistantContextError, AssistantContextProvider, FileSystem, FsError, Issue, MarkdownDoc,
    PreparedContext, Project, RemotePath,
};

const ASSISTANT_DIR: &str = "assistant";
const TICKET_ASSISTANT_FILE: &str = "ticket-assistant.md";
const DEFAULT_TICKET_ASSISTANT_CONTEXT: &str = include_str!("default_ticket_assistant.md");

/// File-backed provider for the IdeA-owned ticket assistant context.
#[derive(Clone)]
pub struct FsAssistantContextStore {
    fs: Arc<dyn FileSystem>,
    app_data_dir: String,
}

impl FsAssistantContextStore {
    /// Builds the store from an injected filesystem and app-data directory.
    #[must_use]
    pub fn new(fs: Arc<dyn FileSystem>, app_data_dir: impl Into<String>) -> Self {
        Self {
            fs,
            app_data_dir: app_data_dir.into(),
        }
    }

    fn join(&self, rel: &str) -> String {
        let base = self.app_data_dir.trim_end_matches(['/', '\\']);
        format!("{base}/{rel}")
    }

    fn context_path(&self) -> RemotePath {
        RemotePath::new(self.join(&format!("{ASSISTANT_DIR}/{TICKET_ASSISTANT_FILE}")))
    }

    async fn read_base_context(&self) -> Result<String, AssistantContextError> {
        let path = self.context_path();
        match self.fs.read(&path).await {
            Ok(bytes) => {
                String::from_utf8(bytes).map_err(|e| AssistantContextError::Store(e.to_string()))
            }
            Err(FsError::NotFound(_)) => Ok(DEFAULT_TICKET_ASSISTANT_CONTEXT.to_owned()),
            Err(e) => Err(AssistantContextError::Store(e.to_string())),
        }
    }
}

#[async_trait]
impl AssistantContextProvider for FsAssistantContextStore {
    async fn prepare_ticket_assistant_context(
        &self,
        project: &Project,
        issue: &Issue,
    ) -> Result<PreparedContext, AssistantContextError> {
        let mut content = self.read_base_context().await?;
        content.push_str("\n\n## Bound Ticket\n\n");
        content.push_str(&format!("- Ref: {}\n", issue.reference()));
        content.push_str(&format!("- Title: {}\n", issue.title));
        content.push_str("\n### Description\n\n");
        content.push_str(issue.description.as_str());
        content.push_str("\n\n### Carnet\n\n");
        content.push_str(issue.carnet.as_str());
        content.push('\n');

        Ok(PreparedContext {
            content: MarkdownDoc::new(content),
            relative_path: TICKET_ASSISTANT_FILE.to_owned(),
            project_root: project.root.as_str().to_owned(),
        })
    }
}
