use axum::{
    extract::Form,
    http::StatusCode,
    response::{Html, IntoResponse},
    Extension, Router,
};
use crate::chat_names_approver::ChatNamesApprover;
use indieweb::standards::{
    vouch::DomainApprover,
    webmention::Request,
};
use minijinja::context;
use std::sync::{Arc, Mutex};
use url::Url;
use reqwest;
use time::{format_description, OffsetDateTime};

#[derive(Clone)]
pub struct SharedState {
    approver: Arc<dyn DomainApprover>,
}

impl Default for SharedState {
    fn default() -> Self {
        Self::new()
    }
}

impl SharedState {
    pub fn new() -> Self {
        let client = reqwest::Client::default();
        let client_wrapper: indieweb::ReqwestClient = client.into();
        Self {
            approver: Arc::new(ChatNamesApprover::with_client(client_wrapper)),
        }
    }
}

fn engine() -> minijinja::Environment<'static> {
    let mut env = minijinja::Environment::new();
    minijinja_embed::load_templates!(&mut env);
    env
}

#[derive(Debug, Clone)]
struct WebmentionRecord {
    source: Url,
    target: Url,
    vouch: Option<Url>,
    status: WebmentionStatus,
    timestamp: OffsetDateTime,
}

#[derive(Debug, Clone)]
enum WebmentionStatus {
    Accepted,
    Rejected(String), // Reason for rejection
}

#[derive(Clone)]
pub struct WebmentionState {
    records: std::sync::Arc<Mutex<Vec<WebmentionRecord>>>,
}

impl Default for WebmentionState {
    fn default() -> Self {
        Self::new()
    }
}

impl WebmentionState {
    pub fn new() -> Self {
        Self {
            records: std::sync::Arc::new(Mutex::new(Vec::new())),
        }
    }

    fn add_record(&self, record: WebmentionRecord) {
        let mut records = self.records.lock().unwrap();
        records.push(record);
    }

    fn get_records(&self) -> Vec<WebmentionRecord> {
        let records = self.records.lock().unwrap();
        records.clone()
    }
}

#[derive(serde::Deserialize, Debug)]
pub struct WebmentionForm {
    source: Url,
    target: Url,
    #[serde(default)]
    vouch: Option<Url>,
}

#[axum::debug_handler]
pub async fn receive_webmention(
    Extension(shared_state): Extension<SharedState>,
    Extension(webmention_state): Extension<WebmentionState>,
    Form(form): Form<WebmentionForm>,
) -> impl IntoResponse {
    let WebmentionForm { source, target, vouch } = form;

    // Create webmention request
    let vouch_vec = vouch.as_ref().map(|v| vec![v.to_string()]).unwrap_or_default();
    let request = Request {
        source,
        target,
        vouch: vouch_vec,
        token: None,
        private: None,
    };

    // Check if target is valid for this server
    if !request.target.as_str().starts_with("http://localhost:8081/") {
        let record = WebmentionRecord {
            source: request.source,
            target: request.target,
            vouch,
            status: WebmentionStatus::Rejected("Invalid target URL".to_string()),
            timestamp: OffsetDateTime::now_utc(),
        };
        webmention_state.add_record(record);

        return (StatusCode::BAD_REQUEST, "Invalid target URL").into_response();
    }

    // Extract sender authority
    let sender_authority = indieweb::standards::vouch::extract_authority(&request.source);

    // Check if sender authority is approved
    let authority_approved = shared_state.approver.is_authority_approved(&sender_authority).await
        .unwrap_or(false);

    let status = if authority_approved {
        // Authority is approved, accept the webmention
        WebmentionStatus::Accepted
    } else if !request.vouch.is_empty() {
        // Authority not approved but vouch provided - in a real implementation,
        // we would validate the vouch URL here
        WebmentionStatus::Rejected("Vouch validation not implemented in example".to_string())
    } else {
        // Authority not approved and no vouch - return 449 Retry With
        let record = WebmentionRecord {
            source: request.source,
            target: request.target,
            vouch,
            status: WebmentionStatus::Rejected("Authority not approved, vouch required".to_string()),
            timestamp: OffsetDateTime::now_utc(),
        };
        webmention_state.add_record(record);

        return (StatusCode::from_u16(449).unwrap(), "Retry With vouch").into_response();
    };

    let record = WebmentionRecord {
        source: request.source,
        target: request.target,
        vouch,
        status,
        timestamp: OffsetDateTime::now_utc(),
    };
    webmention_state.add_record(record);

    (StatusCode::ACCEPTED, "Webmention accepted").into_response()
}

pub async fn list_webmentions(
    Extension(webmention_state): Extension<WebmentionState>,
) -> impl IntoResponse {
    let records = webmention_state.get_records();

    let webmentions: Vec<_> = records.into_iter().map(|record| {
        let status_text = match &record.status {
            WebmentionStatus::Accepted => "✓ Accepted".to_string(),
            WebmentionStatus::Rejected(reason) => format!("✗ Rejected: {}", reason),
        };

        context! {
            source => record.source,
            target => record.target,
            vouch => record.vouch,
            status => status_text,
            timestamp => record.timestamp.format(&format_description::well_known::Rfc3339).unwrap_or_default().to_string(),
        }
    }).collect();

    let env = engine();
    let template = env.get_template("index.html").unwrap();

    Html(template.render(context! { webmentions }).unwrap())
}

pub fn router() -> Router {
    Router::new()
        .route("/", axum::routing::post(receive_webmention))
        .route("/", axum::routing::get(list_webmentions))
}