use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

use super::token::{self, Session};
use crate::lexicons::{self, com_atproto_server};

#[derive(Debug, Serialize)]
struct CreateSessionRequest {
    identifier: String,
    password: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateSessionResponse {
    did: String,
    handle: String,
    access_jwt: String,
    refresh_jwt: String,
}

/// Login to ATProto PDS
pub async fn login(handle: &str, password: &str, pds: &str, is_bot: bool) -> Result<()> {
    let client = reqwest::Client::new();
    let url = lexicons::url(pds, &com_atproto_server::CREATE_SESSION);

    let req = CreateSessionRequest {
        identifier: handle.to_string(),
        password: password.to_string(),
    };

    let account_type = if is_bot { "bot" } else { "user" };
    println!("Logging in to {} as {} ({})...", pds, handle, account_type);

    let res = client
        .post(&url)
        .json(&req)
        .send()
        .await
        .context("Failed to send login request")?;

    if !res.status().is_success() {
        let status = res.status();
        let body = res.text().await.unwrap_or_default();
        anyhow::bail!("Login failed: {} - {}", status, body);
    }

    let session_res: CreateSessionResponse = res.json().await?;

    let session = Session {
        did: session_res.did,
        handle: session_res.handle,
        access_jwt: session_res.access_jwt,
        refresh_jwt: session_res.refresh_jwt,
        pds: Some(pds.to_string()),
    };

    if is_bot {
        token::save_bot_session(&session)?;
    } else {
        token::save_session(&session)?;
    }
    println!("Logged in as {} ({})", session.handle, session.did);

    Ok(())
}

/// Refresh access token
pub async fn refresh_session() -> Result<Session> {
    let session = token::load_session()?;
    let pds = session.pds.as_deref().unwrap_or("bsky.social");

    let client = reqwest::Client::new();
    let url = lexicons::url(pds, &com_atproto_server::REFRESH_SESSION);

    let res = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", session.refresh_jwt))
        .send()
        .await
        .context("Failed to refresh session")?;

    if !res.status().is_success() {
        let status = res.status();
        let body = res.text().await.unwrap_or_default();
        anyhow::bail!("Refresh failed: {} - {}. Try logging in again.", status, body);
    }

    let new_session: CreateSessionResponse = res.json().await?;

    let session = Session {
        did: new_session.did,
        handle: new_session.handle,
        access_jwt: new_session.access_jwt,
        refresh_jwt: new_session.refresh_jwt,
        pds: Some(pds.to_string()),
    };

    token::save_session(&session)?;

    Ok(session)
}

/// Refresh bot access token
pub async fn refresh_bot_session() -> Result<Session> {
    let session = token::load_bot_session()?;
    let pds = session.pds.as_deref().unwrap_or("bsky.social");

    let client = reqwest::Client::new();
    let url = lexicons::url(pds, &com_atproto_server::REFRESH_SESSION);

    let res = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", session.refresh_jwt))
        .send()
        .await
        .context("Failed to refresh bot session")?;

    if !res.status().is_success() {
        let status = res.status();
        let body = res.text().await.unwrap_or_default();
        anyhow::bail!("Bot refresh failed: {} - {}. Try 'ailog login --bot' again.", status, body);
    }

    let new_session: CreateSessionResponse = res.json().await?;

    let session = Session {
        did: new_session.did,
        handle: new_session.handle,
        access_jwt: new_session.access_jwt,
        refresh_jwt: new_session.refresh_jwt,
        pds: Some(pds.to_string()),
    };

    token::save_bot_session(&session)?;

    Ok(session)
}
