use crate::doqium_data_client::apis::attachments_api::list_attachments;
use crate::doqium_data_client::models::Attachment;
use crate::repository::DoqiumClient;
use anyhow::{Context, Result};

pub async fn list_note_attachments(
    token: &str,
    notebook_id: &str,
    note_id: &str,
) -> Result<Vec<Attachment>> {
    let client = DoqiumClient::new(token)?;
    let attachments = list_attachments(client.data_config(), notebook_id, note_id).await?;
    Ok(attachments)
}

pub async fn download_note_attachment(
    token: &str,
    notebook_id: &str,
    note_id: &str,
    attachment_id: &str,
) -> Result<Vec<u8>> {
    let client = DoqiumClient::new(token)?;

    // We need to get the slugs to use the public download endpoint
    // First get the note to get its slug
    let note = crate::apis::notes_api::get_note(token, notebook_id, note_id).await?;

    // Get the notebook to get its slug and workspace_id
    let notebooks = crate::apis::notebooks_api::list_notebooks(token).await?;
    let notebook = notebooks
        .iter()
        .find(|n| n.id == notebook_id)
        .ok_or_else(|| anyhow::anyhow!("Notebook not found"))?;

    // Get the workspace to get its slug
    let workspaces = crate::apis::workspaces_api::list_workspaces(token).await?;
    let workspace = workspaces
        .iter()
        .find(|w| w.id == notebook.workspace_id)
        .ok_or_else(|| anyhow::anyhow!("Workspace not found"))?;

    // Extract username from JWT token
    let username = extract_username_from_token(token)?;

    // Build the public attachment download URL using slugs
    // Format: /api/v1/{username}/{workspaceSlug}/{notebookSlug}/notes/{noteSlug}/attachments/{attachmentId}
    let url = format!(
        "{}/api/v1/{}/{}/{}/notes/{}/attachments/{}",
        client.data_config().base_path,
        username,
        workspace.slug,
        notebook.slug,
        note.metadata.slug,
        attachment_id
    );

    let response = client.data_config().client.get(&url).send().await?;

    if response.status().is_success() {
        let bytes = response.bytes().await?;
        Ok(bytes.to_vec())
    } else {
        anyhow::bail!("Failed to download attachment: {}", response.status())
    }
}

fn extract_username_from_token(token: &str) -> Result<String> {
    use base64::{engine::general_purpose, Engine as _};
    use serde_json::Value;

    // JWT format: header.payload.signature
    let parts: Vec<&str> = token.split('.').collect();
    if parts.len() != 3 {
        anyhow::bail!("Invalid JWT token format");
    }

    // Decode the payload (second part)
    let payload = general_purpose::STANDARD_NO_PAD
        .decode(parts[1])
        .or_else(|_| general_purpose::STANDARD.decode(parts[1]))
        .context("Failed to decode JWT payload")?;

    let payload_str = String::from_utf8(payload).context("Invalid UTF-8 in JWT payload")?;
    let claims: Value = serde_json::from_str(&payload_str).context("Failed to parse JWT claims")?;

    claims
        .get("user")
        .and_then(|u| u.get("username"))
        .and_then(|u| u.as_str())
        .map(String::from)
        .ok_or_else(|| anyhow::anyhow!("Username not found in JWT token"))
}

pub async fn upload_note_attachment(
    token: &str,
    notebook_id: &str,
    note_id: &str,
    file_data: Vec<u8>,
    file_name: &str,
) -> Result<String> {
    let client = DoqiumClient::new(token)?;

    // Create a multipart form with the file and required fields
    let part = reqwest::multipart::Part::bytes(file_data).file_name(file_name.to_string());

    let form = reqwest::multipart::Form::new()
        .part("file", part)
        .text("notebookId", notebook_id.to_string())
        .text("noteId", note_id.to_string())
        .text("fileName", file_name.to_string());

    let url = format!(
        "{}/api/v1/{}/notes/{}/attachments",
        client.data_config().base_path,
        notebook_id,
        note_id
    );

    let response = client
        .data_config()
        .client
        .post(&url)
        .multipart(form)
        .send()
        .await?;

    if response.status().is_success() {
        let attachment: Attachment = response.json().await?;
        Ok(attachment.id.to_string())
    } else {
        anyhow::bail!("Failed to upload attachment: {}", response.status())
    }
}
