//! Webfinger endpoint for ActivityPub actor discovery.
//!
//! `GET /.well-known/webfinger?resource=acct:{user}@{host}` returns a JRD
//! document pointing to the actor URL.

use actix_web::HttpResponse;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
use serde::Serialize;

use crate::entities::prelude::Users;
use crate::entities::users;

use super::ApUrls;

/// JRD (JSON Resource Descriptor) for webfinger.
#[derive(Serialize)]
struct Jrd {
    subject: String,
    links: Vec<Link>,
}

#[derive(Serialize)]
struct Link {
    rel: String,
    r#type: String,
    href: String,
}

/// The actual webfinger logic, separated for testability.
pub async fn webfinger(
    db: &DatabaseConnection,
    resource: &str,
    public_url: &str,
) -> actix_web::HttpResponse {
    if !enabled_from_url(public_url) {
        return actix_web::HttpResponse::NotFound().finish();
    }

    let urls = ApUrls::new(public_url);
    let host = urls.host();

    // Parse the resource parameter: must be "acct:{user}@{host}".
    let Some(user_and_host) = resource.strip_prefix("acct:") else {
        return HttpResponse::BadRequest().body("resource must start with acct:");
    };

    let Some((user, resource_host)) = user_and_host.split_once('@') else {
        return HttpResponse::BadRequest().body("resource must be acct:user@host");
    };

    // Host must match.
    if resource_host != host {
        return HttpResponse::NotFound().finish();
    }

    // Look up the user.
    let user_model = match Users::find()
        .filter(users::Column::Name.eq(user))
        .filter(users::Column::DeletedAt.is_null())
        .one(db)
        .await
    {
        Ok(Some(user)) => user,
        Ok(None) => return HttpResponse::NotFound().finish(),
        Err(_) => return HttpResponse::InternalServerError().finish(),
    };

    let actor_url = urls.actor(&user_model.name);
    let jrd = Jrd {
        subject: format!("acct:{}@{}", user, host),
        links: vec![Link {
            rel: "self".to_string(),
            r#type: "application/activity+json".to_string(),
            href: actor_url,
        }],
    };

    HttpResponse::Ok()
        .content_type("application/jrd+json")
        .json(jrd)
}

fn enabled_from_url(public_url: &str) -> bool {
    !public_url.is_empty()
}
