use super::types::{ActorExt, PublicKey, PublicKeyInner};
use super::util::accept;
use super::{keys, DOMAIN};
use crate::activitypub::db;
use crate::activitypub::types::{ImageExt, ImageExtData};
use activitystreams::actor::{ApActor, Person};
use activitystreams::base::AnyBase;
use activitystreams::collection::UnorderedCollection;
use activitystreams::object::Image;
use activitystreams::prelude::{ApActorExt, ObjectExt};
use activitystreams::{context, iri_string::types::IriString, prelude::*, security};
use axum::http::HeaderMap;
use axum::response::{IntoResponse, Redirect, Response};
use axum::{Extension, Json};
use hyper::{StatusCode, Uri};
use log::error;
use once_cell::sync::Lazy;
use sqlx::SqlitePool;

pub async fn handle(headers: HeaderMap, uri: Uri) -> Response {
    let json = uri
        .query()
        .unwrap_or("")
        .split("&")
        .any(|p| p.split("=").next() == Some("json"));
    if accept::accepts_html(&headers) && !json {
        Redirect::permanent("/").into_response()
    } else {
        (
            [("Content-Type", "application/activity+json")],
            Json(actor()),
        )
            .into_response()
    }
}

pub async fn handle_followers(Extension(pool): Extension<SqlitePool>) -> Response {
    match db::get_followers(&pool).await {
        Ok(followers) => {
            let mut collection = UnorderedCollection::new();
            collection.add_context(context());
            collection.set_id(FOLLOWERS.clone());
            collection.set_total_items(followers.len() as u64);
            collection.set_many_items(
                followers
                    .into_iter()
                    .map(|actor| AnyBase::from_xsd_string(actor.id)),
            );
            Json(collection).into_response()
        }
        Err(e) => {
            error!("Error fetching followers: {:?}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Unable to fetch followers",
            )
                .into_response()
        }
    }
}

pub static ID: Lazy<IriString> = Lazy::new(|| {
    format!("https://{}/ap/actor", *DOMAIN)
        .parse::<IriString>()
        .unwrap()
});
static ICON_URL: Lazy<String> = Lazy::new(|| format!("https://{}/shadowfacts.png", *DOMAIN));
static INBOX: Lazy<IriString> = Lazy::new(|| {
    format!("https://{}/ap/inbox", *DOMAIN)
        .parse::<IriString>()
        .unwrap()
});
pub static FOLLOWERS: Lazy<IriString> = Lazy::new(|| {
    format!("https://{}/ap/actor/followers", *DOMAIN)
        .parse::<IriString>()
        .unwrap()
});
pub static KEY_ID: Lazy<IriString> = Lazy::new(|| {
    format!("https://{}/ap/actor#main-key", *DOMAIN)
        .parse::<IriString>()
        .unwrap()
});

fn actor() -> ActorExt {
    let key = PublicKey {
        public_key: PublicKeyInner {
            id: KEY_ID.clone(),
            owner: ID.clone(),
            public_key_pem: keys::PUB_KEY_PEM.clone(),
        },
    };
    let mut actor = ActorExt::new(ApActor::new(INBOX.clone(), Person::new()), key);
    actor.add_context(context());
    actor.add_context(security());
    actor.set_id(ID.clone());
    actor.set_preferred_username("blog");
    actor.set_name("shadowfacts' blog");
    let image = ImageExt::new(
        Image::new(),
        ImageExtData::new("image/png".into(), ICON_URL.clone()),
    );
    actor.set_icon(image.into_any_base().unwrap());
    actor.set_followers(FOLLOWERS.clone());
    actor
}
