use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;

use crate::{AppService, error::AppError};

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ReadmeDto {
    pub content: String,
    pub html: String,
}

impl AppService {
    pub async fn git_repo_readme(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<Option<ReadmeDto>, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;

        let readme_names = [
            "README.md",
            "README.markdown",
            "README.txt",
            "README",
            "Readme.md",
            "readme.md",
        ];

        for name in &readme_names {
            match self
                .git_tree_entry_by_path_from_commit_for_readme(&repo.id, name)
                .await?
            {
                Some((content, oid)) => {
                    return self
                        .git_blob_load_for_readme(&repo, &content, &oid)
                        .await;
                }
                None => continue,
            }
        }

        Ok(None)
    }
}
impl AppService {
    async fn git_tree_entry_by_path_from_commit_for_readme(
        &self,
        repo_id: &uuid::Uuid,
        readme_name: &str,
    ) -> Result<Option<(String, String)>, AppError> {
        use crate::git::rpc_err;
        use git::rpc::proto as p;
        use git::rpc::proto::tree_service_client::TreeServiceClient;

        let mut client = TreeServiceClient::new(self.git.clone());
        let mut commit_client =
            git::rpc::proto::commit_service_client::CommitServiceClient::new(
                self.git.clone(),
            );
        let summary_resp = commit_client
            .commit_summary(tonic::Request::new(p::CommitSummaryRequest {
                repo_id: repo_id.to_string(),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();

        let head_commit = match summary_resp.summary.and_then(|s| s.head) {
            Some(c) => c,
            None => return Ok(None),
        };

        let tree_id = match head_commit.tree_id {
            Some(id) => id.value,
            None => return Ok(None),
        };

        let resp = client
            .tree_entry_by_path(tonic::Request::new(
                p::TreeEntryByPathRequest {
                    repo_id: repo_id.to_string(),
                    tree_oid: Some(p::ObjectId {
                        value: tree_id.clone(),
                    }),
                    path: readme_name.to_string(),
                },
            ))
            .await
            .map_err(rpc_err)?
            .into_inner();

        match resp.entry {
            Some(entry) => {
                let oid = entry.oid.map(|o| o.value).unwrap_or_default();
                if oid.is_empty() {
                    Ok(None)
                } else {
                    Ok(Some((readme_name.to_string(), oid)))
                }
            }
            None => Ok(None),
        }
    }
    async fn git_blob_load_for_readme(
        &self,
        repo: &model::repos::RepoModel,
        _path: &str,
        oid: &str,
    ) -> Result<Option<super::readme::ReadmeDto>, AppError> {
        use crate::git::rpc_err;
        use git::rpc::proto as p;
        use git::rpc::proto::blob_service_client::BlobServiceClient;

        let mut client = BlobServiceClient::new(self.git.clone());
        let resp = client
            .blob_load(tonic::Request::new(p::BlobLoadRequest {
                repo_id: repo.id.to_string(),
                id: Some(p::ObjectId {
                    value: oid.to_string(),
                }),
                path: String::new(),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();

        let content = String::from_utf8_lossy(&resp.blob).to_string();
        if content.is_empty() {
            return Ok(None);
        }

        let html = comrak::markdown_to_html(
            &content,
            &comrak::ComrakOptions::default(),
        );

        Ok(Some(super::readme::ReadmeDto { content, html }))
    }
}
