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>,
}

/// Query parameters for consent screen
#[derive(Debug, Deserialize)]
pub struct ConsentQuery {
    pub pending_id: String,
}

/// Form data for consent submission
#[derive(Debug, Deserialize)]
pub struct ConsentForm {
    pub action: String,
    pub pending_id: 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 redirects to consent screen
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 a pending authorization ID
    let pending_id = format!("pending_{}", nanoid!(16));
    
    // Create pending authorization
    let pending_auth = crate::server::PendingAuthorization::new(
        pending_id.clone(),
        client_id,
        redirect_uri,
        form.code_challenge.clone(),
        form.code_challenge_method.clone(),
        form.state.clone(),
        Some(me),
        scope,
        user.profile.me.clone(),
        user.profile.name.clone(),
        std::time::Duration::from_secs(600), // 10 minutes
    );

    // Store the pending authorization
    state.store_pending_auth(pending_id.clone(), pending_auth);

    // Redirect to consent screen
    let consent_url = format!("/auth/confirm?pending_id={}", pending_id);
    Ok(Redirect::to(&consent_url))
}

/// GET handler for authorization confirmation (consent screen)
/// Shows the consent screen with client and scope information
pub async fn auth_confirm_get(
    State(state): State<Arc<AppState>>,
    Query(params): Query<ConsentQuery>,
) -> Result<Response, StatusCode> {
    // Retrieve the pending authorization
    let pending = state
        .pending_auths
        .read()
        .unwrap()
        .get(&params.pending_id)
        .cloned();

    let pending = match pending {
        Some(p) if !p.is_expired() => p,
        _ => return Err(StatusCode::BAD_REQUEST),
    };

    // Convert scopes to a list of strings for display
    let scope_vec: Vec<indieweb::standards::indieauth::Scope> = pending.scope.clone().into();
    let requested_scopes: Vec<String> = scope_vec
        .into_iter()
        .map(|s| s.to_string())
        .collect();

    let engine = minijinja::Environment::new();
    let template = engine.render_str(
        include_str!("../templates/confirm.html"),
        minijinja::context! {
            client_id => pending.client_id.as_str(),
            client_name => pending.client_id.as_str(), // Use client_id as name for now
            client_uri => Option::<String>::None,
            client_description => Option::<String>::None,
            redirect_uri => pending.redirect_uri.as_str(),
            state => pending.state,
            me => pending.me.as_ref().map(|u| u.as_str()).unwrap_or(""),
            requested_scopes => requested_scopes,
            user_name => pending.user_name,
            user_url => pending.user_me.as_str(),
            pending_id => params.pending_id,
        }
    ).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

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

/// POST handler for authorization confirmation
/// Processes user consent (approve/deny)
pub async fn auth_confirm(
    State(state): State<Arc<AppState>>,
    Form(form): Form<ConsentForm>,
) -> Result<Redirect, StatusCode> {
    // Retrieve and consume the pending authorization
    let pending = match state.consume_pending_auth(&form.pending_id) {
        Some(p) if !p.is_expired() => p,
        _ => return Err(StatusCode::BAD_REQUEST),
    };

    // Handle denial
    if form.action == "deny" {
        let mut redirect_url = pending.redirect_uri.as_str().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", &pending.state);
        return Ok(Redirect::to(redirect_url.as_str()));
    }

    // Only "approve" action is valid at this point
    if form.action != "approve" {
        return Err(StatusCode::BAD_REQUEST);
    }

    // Generate authorization code
    let code = format!("auth_{}", nanoid!(16));
    let code_data = crate::server::AuthorizationCode::new(
        code.clone(),
        pending.client_id,
        pending.redirect_uri.clone(),
        pending.code_challenge,
        pending.code_challenge_method,
        pending.me,
        pending.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 = pending.redirect_uri.as_str().parse::<Url>()
        .map_err(|_| StatusCode::BAD_REQUEST)?;
    redirect_url
        .query_pairs_mut()
        .append_pair("code", &code)
        .append_pair("state", &pending.state)
        .append_pair("iss", state.issuer.as_str());

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