#[cfg(feature = "http")]
use std::sync::Arc;

#[cfg(feature = "http")]
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
#[cfg(feature = "http")]
use serde::Deserialize;
#[cfg(feature = "http")]
use serde_json::json;

#[cfg(feature = "http")]
use crate::error::SunscreenError;
#[cfg(feature = "http")]
use crate::models::{AddressCheckResult, Currency, SanctionsListResponse};
#[cfg(feature = "http")]
use crate::repositories::{AddressRepository, CountryRepository};

#[cfg(feature = "http")]
use super::AppState;

#[cfg(feature = "http")]
#[derive(Debug, Deserialize)]
pub struct AddressQuery {
    pub address: String,
}

// --- Public endpoint (no auth) ---

/// Combined sanctions list: all addresses (flat) + country codes (flat).
///
/// `GET /v1/lists`
///
/// Response matches the cedros-pay `SanctionsListResponse` contract:
/// ```json
/// {
///   "addresses": ["addr1", "addr2", ...],
///   "countries": ["KP", "IR", "SY", "CU", ...]
/// }
/// ```
#[cfg(feature = "http")]
pub async fn sanctions_lists(
    State(state): State<Arc<AppState>>,
) -> Result<Json<SanctionsListResponse>, AppError> {
    let all = state.storage.address_repo.list_all(None).await?;
    let countries = state.storage.country_repo.list_all().await?;

    // Flat list of all addresses across all chains.
    let addresses: Vec<String> = all.into_iter().map(|a| a.address).collect();

    // Deduplicated ISO country codes only.
    let mut country_codes: Vec<String> = countries
        .into_iter()
        .map(|c| c.code)
        .collect::<std::collections::HashSet<_>>()
        .into_iter()
        .collect();
    country_codes.sort();

    Ok(Json(SanctionsListResponse {
        addresses,
        countries: country_codes,
    }))
}

// --- Authenticated endpoint (requires X-API-Key) ---

/// Check a single address against the sanctions database for a given currency.
///
/// `GET /v1/check/{currency}?address=<addr>`
#[cfg(feature = "http")]
pub async fn check_address(
    Path(currency): Path<String>,
    Query(query): Query<AddressQuery>,
    State(state): State<Arc<AppState>>,
) -> Result<Json<AddressCheckResult>, AppError> {
    let normalized = Currency::normalize(&currency);
    let result = state
        .storage
        .address_repo
        .check_address(normalized.as_str(), &query.address)
        .await?;

    match result {
        Some(source_type) => Ok(Json(AddressCheckResult::found(
            query.address,
            source_type,
        ))),
        None => Ok(Json(AddressCheckResult::not_found(query.address))),
    }
}

// --- Error handling ---

#[cfg(feature = "http")]
pub struct AppError {
    status: StatusCode,
    message: String,
}

#[cfg(feature = "http")]
impl From<SunscreenError> for AppError {
    fn from(error: SunscreenError) -> Self {
        match &error {
            SunscreenError::InvalidRequest(_) => Self {
                status: StatusCode::BAD_REQUEST,
                message: error.to_string(),
            },
            SunscreenError::Unauthorized(_) => Self {
                status: StatusCode::UNAUTHORIZED,
                message: error.to_string(),
            },
            SunscreenError::NotFound => Self {
                status: StatusCode::NOT_FOUND,
                message: error.to_string(),
            },
            SunscreenError::MissingEnvVar(_)
            | SunscreenError::DataSourceFetch(_)
            | SunscreenError::Database(_)
            | SunscreenError::Migration(_)
            | SunscreenError::Json(_)
            | SunscreenError::HttpClient(_) => {
                tracing::error!(detail = %error, "sunscreen request failed");
                Self {
                    status: StatusCode::INTERNAL_SERVER_ERROR,
                    message: "internal server error".into(),
                }
            }
        }
    }
}

#[cfg(feature = "http")]
impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        (self.status, Json(json!({ "error": self.message }))).into_response()
    }
}
