use std::{collections::HashMap, str::FromStr};

use axum::{
    debug_handler,
    extract::{Path, State},
    response::Response,
};
// [`axum_extra`]'s form extractor is required to support repeated keys:
// https://docs.rs/axum-extra/0.10.1/axum_extra/extract/struct.Form.html#differences-from-axumextractform
use axum_extra::extract::Form;
use interim_models::{
    field_form_prompt::FieldFormPrompt, language::Language, portal::Portal, workspace::Workspace,
};
use serde::Deserialize;
use sqlx::postgres::types::Oid;
use uuid::Uuid;

use crate::{
    app::{App, AppDbConn},
    errors::{AppError, bad_request},
    navigator::{Navigator, NavigatorPage as _},
    user::CurrentUser,
    workspace_pooler::{RoleAssignment, WorkspacePooler},
};

#[derive(Debug, Deserialize)]
pub(super) struct PathParams {
    portal_id: Uuid,
    rel_oid: u32,
    workspace_id: Uuid,
}

/// HTTP POST handler for setting form prompt content on all fields within a
/// single [`Portal`]. The form body is expected to be an HTTP form encoded
/// mapping of `<FIELD_ID>.<LANGUAGE_CODE>` to content.
///
/// Upon success, the client is redirected back to the portal's form editor
/// page.
#[debug_handler(state = App)]
pub(super) async fn post(
    State(mut workspace_pooler): State<WorkspacePooler>,
    AppDbConn(mut app_db): AppDbConn,
    CurrentUser(user): CurrentUser,
    navigator: Navigator,
    Path(PathParams {
        portal_id,
        rel_oid,
        workspace_id,
    }): Path<PathParams>,
    Form(form): Form<HashMap<String, String>>,
) -> Result<Response, AppError> {
    // FIXME: Check workspace authorization.
    // FIXME ensure workspace corresponds to rel/portal, and that user has
    // permission to access/alter both as needed.
    // FIXME CSRF

    let portal = Portal::with_id(portal_id).fetch_one(&mut app_db).await?;
    let workspace = Workspace::with_id(portal.workspace_id)
        .fetch_one(&mut app_db)
        .await?;

    let mut workspace_client = workspace_pooler
        .acquire_for(workspace.id, RoleAssignment::User(user.id))
        .await?;

    // FIXME assert that fields all belong to the authorized portal

    for (name, content) in form {
        let mut name_split = name.split('.');
        let field_id = name_split
            .next()
            .and_then(|value| Uuid::parse_str(value).ok())
            .ok_or(bad_request!("expected input name to start with <FIELD_ID>"))?;
        let language = name_split
            .next()
            .and_then(|value| Language::from_str(value).ok())
            .ok_or(bad_request!(
                "expected input name to be <FIELD_ID>.<LANGUAGE_CODE>"
            ))?;
        if name_split.next().is_some() {
            return Err(bad_request!("input name longer than expected"));
        }
        FieldFormPrompt::upsert()
            .field_id(field_id)
            .language(language)
            .content(content)
            .build()?
            .execute(&mut app_db)
            .await?;
    }

    // FIXME redirect to the correct page
    Ok(navigator
        .portal_page()
        .workspace_id(workspace_id)
        .rel_oid(Oid(rel_oid))
        .portal_id(portal_id)
        .build()?
        .redirect_to())
}
