use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use aws_local_core::db::DbPool;
use chrono::Utc;
use serde_json::Value;
use uuid::Uuid;

use crate::docker::{DockerManager, engine_from_params};
use crate::errors::RdsError;

pub async fn create_db_cluster(
    body: Value,
    db: &DbPool,
    docker: &DockerManager,
) -> Result<Response, RdsError> {
    let identifier = body["DBClusterIdentifier"]
        .as_str()
        .ok_or_else(|| RdsError::InvalidInput("DBClusterIdentifier required".into()))?;
    let engine = body["Engine"].as_str().unwrap_or("aurora-postgresql");
    let engine_version = body["EngineVersion"].as_str().unwrap_or("16");
    let master_username = body["MasterUsername"]
        .as_str()
        .ok_or_else(|| RdsError::InvalidInput("MasterUsername required".into()))?;
    let master_password = body["MasterUserPassword"]
        .as_str()
        .ok_or_else(|| RdsError::InvalidInput("MasterUserPassword required".into()))?;
    let db_name = body["DatabaseName"].as_str().unwrap_or(identifier);

    let db_engine = engine_from_params(engine, engine_version);

    // Try Docker-based creation; fall back to metadata-only stub if Docker
    // is unavailable or the container fails to start.
    let port = match docker.ensure_container(&db_engine).await {
        Ok(p) => {
            if let Err(e) = docker
                .create_database_for_engine(&db_engine, p, db_name, master_username, master_password)
                .await
            {
                tracing::warn!("Docker create_database failed, using stub: {e}");
            }
            p
        }
        Err(e) => {
            tracing::warn!("Docker unavailable for RDS cluster, using stub: {e}");
            5432
        }
    };

    let password_hash = hash_password(master_password);
    let now = Utc::now().to_rfc3339();
    let endpoint = "localhost";

    {
        let conn = db.lock().unwrap();
        conn.execute(
            "INSERT INTO db_clusters
             (db_cluster_identifier, engine, engine_version,
              master_username, password_hash, database_name,
              status, endpoint_address, endpoint_port, created_at)
             VALUES (?1,?2,?3,?4,?5,?6,'available',?7,?8,?9)",
            rusqlite::params![
                identifier, engine, engine_version,
                master_username, password_hash, db_name,
                endpoint, port, now,
            ],
        ).map_err(|e| {
            if e.to_string().contains("UNIQUE") {
                RdsError::AlreadyExists(format!("DB cluster {identifier} already exists"))
            } else {
                RdsError::InternalError(e.to_string())
            }
        })?;
    }

    Ok(cluster_xml_response(
        "CreateDBCluster",
        &cluster_xml(identifier, engine, engine_version, master_username, db_name, "available", endpoint, port, &now),
    ))
}

pub fn describe_db_clusters(
    body: Value,
    db: &DbPool,
) -> Result<Response, RdsError> {
    let conn = db.lock().unwrap();
    let identifier = body["DBClusterIdentifier"].as_str();

    if let Some(id) = identifier {
        let mut stmt = conn.prepare(
            "SELECT db_cluster_identifier, engine, engine_version,
                    master_username, database_name, status,
                    endpoint_address, endpoint_port, created_at
             FROM db_clusters WHERE db_cluster_identifier = ?1"
        ).map_err(|e| RdsError::InternalError(e.to_string()))?;
        let clusters: Vec<String> = stmt.query_map(rusqlite::params![id], cluster_row_to_xml)
            .map_err(|e| RdsError::InternalError(e.to_string()))?
            .filter_map(|r| r.ok())
            .collect();
        if clusters.is_empty() {
            return Err(RdsError::ResourceNotFound(format!("DB cluster {id} not found")));
        }
        let members = clusters.join("");
        return Ok(cluster_xml_response(
            "DescribeDBClusters",
            &format!("<DBClusters>{members}</DBClusters>"),
        ));
    }

    let mut stmt = conn.prepare(
        "SELECT db_cluster_identifier, engine, engine_version,
                master_username, database_name, status,
                endpoint_address, endpoint_port, created_at
         FROM db_clusters ORDER BY db_cluster_identifier"
    ).map_err(|e| RdsError::InternalError(e.to_string()))?;
    let clusters: Vec<String> = stmt.query_map([], cluster_row_to_xml)
        .map_err(|e| RdsError::InternalError(e.to_string()))?
        .filter_map(|r| r.ok())
        .collect();
    let members = clusters.join("");

    Ok(cluster_xml_response(
        "DescribeDBClusters",
        &format!("<DBClusters>{members}</DBClusters>"),
    ))
}

pub async fn delete_db_cluster(
    body: Value,
    db: &DbPool,
    docker: &DockerManager,
    drop_on_delete: bool,
) -> Result<Response, RdsError> {
    let identifier = body["DBClusterIdentifier"]
        .as_str()
        .ok_or_else(|| RdsError::InvalidInput("DBClusterIdentifier required".into()))?;

    let row = {
        let conn = db.lock().unwrap();
        let row = conn.query_row(
            "SELECT database_name, master_username, endpoint_port, engine, engine_version
             FROM db_clusters WHERE db_cluster_identifier=?1",
            rusqlite::params![identifier],
            |r| Ok((
                r.get::<_, String>(0)?,
                r.get::<_, String>(1)?,
                r.get::<_, u16>(2)?,
                r.get::<_, String>(3)?,
                r.get::<_, String>(4)?,
            )),
        ).map_err(|_| RdsError::ResourceNotFound(format!("DB cluster {identifier} not found")))?;

        conn.execute(
            "DELETE FROM db_clusters WHERE db_cluster_identifier=?1",
            rusqlite::params![identifier],
        ).map_err(|e| RdsError::InternalError(e.to_string()))?;

        row
    };

    if drop_on_delete {
        let db_engine = engine_from_params(&row.3, &row.4);
        docker.drop_database_for_engine(&db_engine, row.2, &row.0, &row.1).await
            .map_err(RdsError::InternalError)?;
    }

    Ok(cluster_xml_response(
        "DeleteDBCluster",
        &format!(
            "<DBCluster>\
               <DBClusterIdentifier>{identifier}</DBClusterIdentifier>\
               <Status>deleting</Status>\
             </DBCluster>"
        ),
    ))
}

fn cluster_row_to_xml(r: &rusqlite::Row) -> rusqlite::Result<String> {
    Ok(cluster_xml(
        &r.get::<_,String>(0)?, &r.get::<_,String>(1)?,
        &r.get::<_,String>(2)?, &r.get::<_,String>(3)?,
        &r.get::<_,String>(4)?, &r.get::<_,String>(5)?,
        &r.get::<_,String>(6)?, r.get::<_,u16>(7)?,
        &r.get::<_,String>(8)?,
    ))
}

fn cluster_xml(
    identifier: &str, engine: &str, version: &str,
    username: &str, db_name: &str, status: &str,
    address: &str, port: u16, created: &str,
) -> String {
    let arn = format!("arn:aws:rds:us-east-1:000000000000:cluster:{identifier}");
    format!(
        "<DBCluster>\
           <DBClusterIdentifier>{identifier}</DBClusterIdentifier>\
           <DBClusterArn>{arn}</DBClusterArn>\
           <Engine>{engine}</Engine>\
           <EngineVersion>{version}</EngineVersion>\
           <EngineMode>provisioned</EngineMode>\
           <MasterUsername>{username}</MasterUsername>\
           <DatabaseName>{db_name}</DatabaseName>\
           <Status>{status}</Status>\
           <Endpoint>{address}</Endpoint>\
           <ReaderEndpoint>{address}</ReaderEndpoint>\
           <Port>{port}</Port>\
           <ClusterCreateTime>{created}</ClusterCreateTime>\
           <StorageEncrypted>true</StorageEncrypted>\
           <DeletionProtection>false</DeletionProtection>\
           <HttpEndpointEnabled>false</HttpEndpointEnabled>\
           <CopyTagsToSnapshot>false</CopyTagsToSnapshot>\
           <CrossAccountClone>false</CrossAccountClone>\
           <IAMDatabaseAuthenticationEnabled>false</IAMDatabaseAuthenticationEnabled>\
           <MultiAZ>false</MultiAZ>\
           <DBClusterMembers/>\
           <VpcSecurityGroups/>\
           <AvailabilityZones>\
             <AvailabilityZone>us-east-1a</AvailabilityZone>\
           </AvailabilityZones>\
           <DBSubnetGroup>default</DBSubnetGroup>\
           <TagList/>\
         </DBCluster>"
    )
}

fn cluster_xml_response(action: &str, result_xml: &str) -> Response {
    let xml = format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
         <{action}Response xmlns=\"http://rds.amazonaws.com/doc/2014-10-31/\">\
         <{action}Result>{result_xml}</{action}Result>\
         <ResponseMetadata><RequestId>{}</RequestId></ResponseMetadata>\
         </{action}Response>",
        Uuid::new_v4(),
    );
    (StatusCode::OK, [("content-type", "text/xml")], xml).into_response()
}

fn hash_password(password: &str) -> String {
    use argon2::{Argon2, PasswordHasher};
    use argon2::password_hash::SaltString;
    let salt = SaltString::generate(&mut rand_core_old::OsRng);
    Argon2::default()
        .hash_password(password.as_bytes(), &salt)
        .map(|h| h.to_string())
        .unwrap_or_else(|_| "hash-failed".to_string())
}
