use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Json;
use hyper::header;
use magnetar_common::config::MagnetarConfig;
use magnetar_common::util::{lenient_parse_acct_decode, FediverseTag};
use magnetar_core::web_model::acct::Acct;
use magnetar_core::web_model::content_type::{
    ContentActivityJson, ContentActivityStreams, ContentHtml, ContentJrdJson,
};
use magnetar_core::web_model::rel::{RelOStatusSubscribe, RelSelf, RelWebFingerProfilePage};
use magnetar_model::CalckeyModel;
use magnetar_webfinger::webfinger::{WebFinger, WebFingerRel, WebFingerSubject};
use serde::Deserialize;
use tracing::error;
use url::Url;

#[derive(Deserialize)]
pub struct WebFingerQuery {
    resource: WebFingerSubject,
    rel: Option<Vec<String>>,
}

// TODO: We don't have this endpoint in Magnetar yet, but make sure it's not hardcoded when
// we do
const USER_AP_ENDPOINT: &str = "/users/";

// TODO: Filter by rel
pub async fn handle_webfinger(
    Query(WebFingerQuery { resource, .. }): Query<WebFingerQuery>,
    State((config, ck)): State<(&'static MagnetarConfig, CalckeyModel)>,
) -> Result<impl IntoResponse, StatusCode> {
    let resource = match resource {
        acct @ WebFingerSubject::Acct(_) => acct,
        // Leniently re-add the acct
        WebFingerSubject::Url(url) if !url.starts_with("http:") && !url.starts_with("https:") => {
            WebFingerSubject::Acct(Acct::new(url.into()))
        }
        other => other,
    };

    let user = match resource {
        WebFingerSubject::Acct(acct) => {
            let tag = lenient_parse_acct_decode(&acct).map_err(|e| {
                error!("Failed to parse tag: {e}");
                StatusCode::UNPROCESSABLE_ENTITY
            })?;

            ck.get_user_by_tag(
                tag.name.as_ref(),
                tag.host
                    .as_ref()
                    .filter(|host| host.to_string() != config.networking.host),
            )
            .await
            .map_err(|e| {
                error!("Data error: {e}");
                StatusCode::INTERNAL_SERVER_ERROR
            })?
        }
        WebFingerSubject::Url(url) => {
            let object_url = url.parse::<Url>().map_err(|e| {
                error!("URL parse error: {e}");
                StatusCode::UNPROCESSABLE_ENTITY
            })?;

            // FIXME: Jank
            let path = object_url.path().strip_prefix(USER_AP_ENDPOINT);
            match path {
                Some(user_id) if !user_id.is_empty() && user_id.chars().all(|c| c != '/') => {
                    ck.get_user_by_id(user_id).await.map_err(|e| {
                        error!("Data error: {e}");
                        StatusCode::INTERNAL_SERVER_ERROR
                    })?
                }
                _ => ck.get_user_by_uri(&url).await.map_err(|e| {
                    error!("Data error: {e}");
                    StatusCode::INTERNAL_SERVER_ERROR
                })?,
            }
        }
    };

    let Some(user) = user else {
        return Err(StatusCode::NOT_FOUND);
    };

    let tag = FediverseTag::from_parts((
        &user.username,
        user.host.as_deref().or(Some(&config.networking.host)),
    ))
    .map_err(|e| {
        error!("URL parse error: {e}");
        StatusCode::INTERNAL_SERVER_ERROR
    })?;

    let mut links = Vec::new();
    let mut aliases = Vec::new();

    if user.host.is_some() {
        if let Some(uri) = user.uri {
            links.push(WebFingerRel::RelSelf {
                rel: RelSelf,
                content_type: ContentActivityStreams,
                href: uri.clone(),
            });

            links.push(WebFingerRel::RelSelfAlt {
                rel: RelSelf,
                content_type: ContentActivityJson,
                href: uri,
            });
        }
    } else {
        links.push(WebFingerRel::RelOStatusSubscribe {
            rel: RelOStatusSubscribe,
            template: format!(
                "{}://{}/authorize-follow?acct={{uri}}",
                config.networking.protocol, config.networking.host
            ),
        });

        let user_url = format!(
            "{}://{}/@{}",
            config.networking.protocol, config.networking.host, tag.name
        );

        links.push(WebFingerRel::RelWebFingerProfilePage {
            rel: RelWebFingerProfilePage,
            content_type: ContentHtml,
            href: user_url.clone(),
        });

        aliases.push(WebFingerSubject::Url(user_url));

        let self_url = format!(
            "{}://{}{}{}",
            config.networking.protocol, config.networking.host, USER_AP_ENDPOINT, user.id
        );

        links.push(WebFingerRel::RelSelf {
            rel: RelSelf,
            content_type: ContentActivityStreams,
            href: self_url.clone(),
        });

        links.push(WebFingerRel::RelSelfAlt {
            rel: RelSelf,
            content_type: ContentActivityJson,
            href: self_url,
        });
    }

    Ok((
        [(header::CONTENT_TYPE, ContentJrdJson.as_ref())],
        Json(WebFinger {
            subject: WebFingerSubject::Acct(tag.into()),
            aliases: Some(aliases),
            links,
        }),
    ))
}
