Open source Nest implementation
use crate::database::Database;
use rocket::serde::{Deserialize, Serialize};
use sqlx::query_as;

#[derive(Debug, Clone, Deserialize, Serialize, sqlx::FromRow)]
#[serde(crate = "rocket::serde")]
pub struct Project {
    pub id: i32,
    pub owner_id: i32,
    pub name: String,
    repository_uuid: uuid::Uuid, // Used for the repository path
}

pub async fn find(db: &Database, org_path: String, proj_path: String) -> Option<Project> {
    let result = sqlx::query_as!(Project,
        "SELECT * FROM projects WHERE name = $1 AND owner_id = (SELECT id FROM users WHERE name = $2)",
        proj_path, org_path).fetch_optional(db)
        .await.ok()?;

    result
}

pub async fn list(db: &Database) -> Result<Vec<Project>, sqlx::Error> {
    sqlx::query_as!(Project, "SELECT * FROM projects")
        .fetch_all(db)
        .await
}

pub async fn create(
    db: &Database,
    owner_id: i32,
    name: String,
) -> Result<Project, sqlx::Error> {
    let result = query_as!(
        Project,
        "INSERT INTO projects (owner_id, name) VALUES ($1, $2) RETURNING *",
        owner_id,
        name.to_lowercase(),
    )
    .fetch_one(db)
    .await?;

    Ok(result)
}

use crate::models::pijul::repositories::Repository;
use crate::models::users::User;
use anyhow::Result;
use std::path::PathBuf;

impl Project {
    pub async fn organisation(&self, db: &Database) -> Result<User> {
        self.owner(db).await
    }

    pub async fn owner(&self, db: &Database) -> Result<User> {
        let u = User::find(db, self.owner_id).await?;

        Ok(u)
    }

    pub fn repository(&self, root: &PathBuf) -> Result<Repository> {
        Ok(Repository::init_or_open(self, root)?)
    }

    pub fn repo_path(&self) -> std::path::PathBuf {
        self.repository_uuid
            .to_hyphenated()
            .to_string()
            .split("-")
            .collect()
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DispProject {
    pub name: String,
    pub organisation: User,
    pub uri: String,
}

impl DispProject {
    pub async fn new(db: &Database, project: Project) -> Self {
        let org = project.organisation(db).await.unwrap();
        let uri = rocket::uri!(crate::controllers::projects::show(&org.name, &project.name));

        DispProject {
            name: project.name,
            organisation: org,
            uri: uri.to_string(),
        }
    }
}