use anyhow::Result;
use askama::Template;
use derive_builder::Builder;
use phono_backends::client::WorkspaceClient;
use phono_models::{client::AppDbClient, workspace::Workspace};
use sqlx::postgres::types::Oid;
use uuid::Uuid;

use crate::{
    navigator::Navigator,
    workspaces::{RelationPortalSet, fetch_all_accessible_portals},
};

#[derive(Builder, Clone, Debug, Template)]
#[template(path = "includes/workspace_nav.html")]
pub(crate) struct WorkspaceNav {
    workspace: Workspace,
    relations: Vec<RelationItem>,
    #[builder(default, setter(strip_option))]
    current: Option<NavLocation>,
    navigator: Navigator,
}

impl WorkspaceNav {
    pub fn builder() -> WorkspaceNavBuilder {
        WorkspaceNavBuilder::default()
    }
}

#[derive(Clone, Debug)]
pub struct RelationItem {
    pub name: String,
    pub oid: Oid,
    pub portals: Vec<PortalItem>,
}

#[derive(Clone, Debug)]
pub struct PortalItem {
    pub name: String,
    pub id: Uuid,
}

#[derive(Clone, Debug, PartialEq)]
pub enum NavLocation {
    Rel(Oid, Option<RelLocation>),
}

#[derive(Clone, Debug, PartialEq)]
pub enum RelLocation {
    Portal(Uuid),
    Sharing,
}

impl WorkspaceNavBuilder {
    /// Helper function to populate relations and lenses automatically.
    /// [`WorkspaceNavBuilder::workspace()`] must be called first, or else this
    /// method will return an error.
    ///
    /// WARNING: This assumes that `workspace_client` is authenticated with
    /// [`RoleAssignment::User`] for the current user.
    pub async fn populate_rels(
        &mut self,
        app_db: &mut AppDbClient,
        workspace_client: &mut WorkspaceClient,
    ) -> Result<&mut Self> {
        let workspace_id = self
            .workspace
            .clone()
            .ok_or(WorkspaceNavBuilderError::UninitializedField("workspace"))?
            .id;
        Ok(self.relations(
            fetch_all_accessible_portals(workspace_id, app_db, workspace_client)
                .await?
                .into_iter()
                .map(|RelationPortalSet { rel, portals }| RelationItem {
                    name: rel.relname,
                    oid: rel.oid,
                    portals: portals
                        .into_iter()
                        .map(|portal| PortalItem {
                            id: portal.id,
                            name: portal.name,
                        })
                        .collect(),
                })
                .collect(),
        ))
    }
}
