use axum::{
    extract::{Query, State},
    http::StatusCode,
    response::{Html, IntoResponse, Redirect, Response},
    Form,
};
use indieweb::standards::indieauth::{ClientId, RedirectUri, Scopes};
use nanoid::nanoid;
use serde::Deserialize;
use std::sync::Arc;
use url::Url;

use crate::state::AppState;

/// Query parameters for authorization request
#[derive(Debug, Deserialize)]
pub struct AuthRequest {
    pub response_type: String,
    pub client_id: String,
    pub redirect_uri: String,
    pub state: String,
    pub code_challenge: String,
    pub code_challenge_method: String,
    pub scope: Option<String>,
    pub me: Option<String>,
}

/// Form data for login submission
#[derive(Debug, Deserialize)]
pub struct LoginForm {
    pub username: String,
    pub password: String,
    pub client_id: String,
    pub redirect_uri: String,
    pub state: String,
    pub code_challenge: String,
    pub code_challenge_method: String,
    pub me: Option<String>,
    pub scope: Option<String>,
}

/// GET handler for authorization endpoint
/// Shows login form if user not authenticated, or consent screen if authenticated
pub async fn auth_get(
    State(_state): State<Arc<AppState>>,
    Query(params): Query<AuthRequest>,
) -> Result<Response, StatusCode> {
    // Validate required parameters
    if params.response_type != "code" {
        return Err(StatusCode::BAD_REQUEST);
    }

    // Parse and validate client_id
    let _client_id = match ClientId::new(&params.client_id) {
        Ok(id) => id,
        Err(_) => return Err(StatusCode::BAD_REQUEST),
    };

    // Parse redirect_uri
    let _redirect_uri = match params.redirect_uri.parse::<Url>() {
        Ok(uri) => RedirectUri::from(uri),
        Err(_) => return Err(StatusCode::BAD_REQUEST),
    };

    // Parse scope if provided
    let _scope = match &params.scope {
        Some(s) => match s.parse::<Scopes>() {
            Ok(scopes) => scopes,
            Err(_) => return Err(StatusCode::BAD_REQUEST),
        },
        None => Scopes::default(),
    };

    // Parse me parameter if provided
    let _me = match &params.me {
        Some(m) => match m.parse::<Url>() {
            Ok(url) => Some(url),
            Err(_) => return Err(StatusCode::BAD_REQUEST),
        },
        None => None,
    };

    // For demo purposes, we'll show the login form directly
    // In a real implementation, you'd check for existing session/authentication
    
    let engine = minijinja::Environment::new();
    let template = engine.render_str(
        include_str!("../templates/auth.html"),
        minijinja::context! {
            client_id => params.client_id,
            redirect_uri => params.redirect_uri,
            state => params.state,
            code_challenge => params.code_challenge,
            code_challenge_method => params.code_challenge_method,
            me => params.me.unwrap_or_default(),
            scope => params.scope.unwrap_or_default(),
        }
    ).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Html(template).into_response())
}

/// POST handler for authorization endpoint
/// Processes login and generates authorization code
pub async fn auth_post(
    State(state): State<Arc<AppState>>,
    Form(form): Form<LoginForm>,
) -> Result<Redirect, StatusCode> {
    // Validate user credentials
    let user = match state.validate_user(&form.username, &form.password) {
        Some(user) => user,
        None => return Err(StatusCode::UNAUTHORIZED),
    };

    // Parse and validate client_id
    let client_id = match ClientId::new(&form.client_id) {
        Ok(id) => id,
        Err(_) => return Err(StatusCode::BAD_REQUEST),
    };

    // Parse redirect_uri
    let redirect_uri = match form.redirect_uri.parse::<Url>() {
        Ok(uri) => RedirectUri::from(uri),
        Err(_) => return Err(StatusCode::BAD_REQUEST),
    };

    // Parse scope if provided
    let scope = match &form.scope {
        Some(s) if !s.is_empty() => match s.parse::<Scopes>() {
            Ok(scopes) => scopes,
            Err(_) => return Err(StatusCode::BAD_REQUEST),
        },
        _ => Scopes::default(),
    };

    // Parse me parameter if provided, otherwise use user's profile URL
    let me = match &form.me {
        Some(m) if !m.is_empty() => match m.parse::<Url>() {
            Ok(url) => url,
            Err(_) => user.profile.me.clone(),
        },
        _ => user.profile.me.clone(),
    };

    // Generate authorization code
    let code = format!("auth_{}", nanoid!(16));
    let code_data = crate::server::AuthorizationCode::new(
        code.clone(),
        client_id,
        redirect_uri.clone(),
        form.code_challenge.clone(),
        form.code_challenge_method.clone(),
        Some(me.clone()),
        scope,
        std::time::Duration::from_secs(600), // 10 minutes
    );

    // Store the authorization code
    state.store_code(code.clone(), code_data);

    // Build redirect URL with code, state, and iss parameters
    let mut redirect_url = form.redirect_uri.parse::<Url>().map_err(|_| StatusCode::BAD_REQUEST)?;
    redirect_url
        .query_pairs_mut()
        .append_pair("code", &code)
        .append_pair("state", &form.state)
        .append_pair("iss", state.issuer.as_str());

    Ok(Redirect::to(redirect_url.as_str()))
}

/// POST handler for authorization confirmation
/// Processes user consent (approve/deny)
pub async fn auth_confirm(
    State(state): State<Arc<AppState>>,
    Form(form): Form<serde_json::Value>,
) -> Result<Redirect, StatusCode> {
    // Extract form data
    let action = form.get("action").and_then(|v| v.as_str()).unwrap_or("");
    let code = form.get("code").and_then(|v| v.as_str()).unwrap_or("");
    let redirect_uri = form.get("redirect_uri").and_then(|v| v.as_str()).unwrap_or("");
    let req_state = form.get("state").and_then(|v| v.as_str()).unwrap_or("");

    if action == "deny" {
        // User denied the request - redirect with error
        let mut redirect_url = redirect_uri.parse::<Url>().map_err(|_| StatusCode::BAD_REQUEST)?;
        redirect_url
            .query_pairs_mut()
            .append_pair("error", "access_denied")
            .append_pair("error_description", "User denied the authorization request")
            .append_pair("state", req_state);
        return Ok(Redirect::to(redirect_url.as_str()));
    }

    if action != "approve" {
        return Err(StatusCode::BAD_REQUEST);
    }

    // Retrieve the stored authorization code
    let code_data = match state.consume_code(&code) {
        Some(data) => data,
        None => return Err(StatusCode::BAD_REQUEST),
    };

    // Verify the code hasn't expired
    if code_data.is_expired() {
        return Err(StatusCode::BAD_REQUEST);
    }

    // Build redirect URL with code, state, and iss parameters
    let mut redirect_url = redirect_uri.parse::<Url>().map_err(|_| StatusCode::BAD_REQUEST)?;
    redirect_url
        .query_pairs_mut()
        .append_pair("code", &code)
        .append_pair("state", &req_state)
        .append_pair("iss", state.issuer.as_str());

    Ok(Redirect::to(redirect_url.as_str()))
}