use db::sqlx;
use session::Session;

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

impl AppService {
    pub async fn git_repo_star(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<serde_json::Value, AppError> {
        let user_uid = session_user(ctx)?;
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;

        sqlx::query(
            "INSERT INTO repo_star (repo, \"user\", created_at) VALUES ($1, $2, $3) \
             ON CONFLICT (repo, \"user\") DO NOTHING",
        )
        .bind(repo.id)
        .bind(user_uid)
        .bind(chrono::Utc::now())
        .execute(self.db.writer())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        let count: (i64,) =
            sqlx::query_as("SELECT COUNT(*) FROM repo_star WHERE repo = $1")
                .bind(repo.id)
                .fetch_one(self.db.reader())
                .await
                .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(serde_json::json!({ "starred": true, "count": count.0 }))
    }

    pub async fn git_repo_unstar(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<serde_json::Value, AppError> {
        let user_uid = session_user(ctx)?;
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;

        sqlx::query("DELETE FROM repo_star WHERE repo = $1 AND \"user\" = $2")
            .bind(repo.id)
            .bind(user_uid)
            .execute(self.db.writer())
            .await
            .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        let count: (i64,) =
            sqlx::query_as("SELECT COUNT(*) FROM repo_star WHERE repo = $1")
                .bind(repo.id)
                .fetch_one(self.db.reader())
                .await
                .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(serde_json::json!({ "starred": false, "count": count.0 }))
    }

    pub async fn git_repo_star_status(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<serde_json::Value, AppError> {
        let user_uid = session_user(ctx)?;
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;

        let exists: (bool,) = sqlx::query_as(
            "SELECT EXISTS(SELECT 1 FROM repo_star WHERE repo = $1 AND \"user\" = $2)",
        )
        .bind(repo.id)
        .bind(user_uid)
        .fetch_one(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        let count: (i64,) =
            sqlx::query_as("SELECT COUNT(*) FROM repo_star WHERE repo = $1")
                .bind(repo.id)
                .fetch_one(self.db.reader())
                .await
                .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(serde_json::json!({
            "starred": exists.0,
            "count": count.0,
        }))
    }
}
