pub mod clusters;
pub mod instances;

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

use crate::docker::DockerManager;

/// Build a standard RDS XML response envelope.
fn 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()
}

pub async fn dispatch(
    action: &str,
    body: Value,
    db: &DbPool,
    docker: &DockerManager,
    drop_on_delete: bool,
) -> Response {
    match action {
        "CreateDBInstance" =>
            instances::create_db_instance(body, db, docker).await,
        "DescribeDBInstances" =>
            instances::describe_db_instances(body, db),
        "DeleteDBInstance" =>
            instances::delete_db_instance(body, db, docker, drop_on_delete).await,
        "CreateDBCluster" =>
            clusters::create_db_cluster(body, db, docker).await,
        "DescribeDBClusters" =>
            clusters::describe_db_clusters(body, db),
        "DeleteDBCluster" =>
            clusters::delete_db_cluster(body, db, docker, drop_on_delete).await,
        // Parameter group operations — these are metadata-only (no Docker
        // containers needed), so we return synthetic XML responses.
        "CreateDBParameterGroup" => return create_db_parameter_group(&body),
        "CreateDBClusterParameterGroup" => return create_db_cluster_parameter_group(&body),
        "DescribeDBParameterGroups" => return describe_db_parameter_groups(&body),
        "DescribeDBClusterParameterGroups" => return describe_db_cluster_parameter_groups(&body),
        "DescribeDBParameters" | "DescribeDBClusterParameters" =>
            return xml_response(action, "<Parameters/>"),
        "ModifyDBParameterGroup" | "ModifyDBClusterParameterGroup" =>
            return xml_response(action, ""),
        "DeleteDBParameterGroup" | "DeleteDBClusterParameterGroup" =>
            return xml_response(action, ""),
        "CreateDBSubnetGroup" => return create_db_subnet_group(&body),
        "DescribeDBSubnetGroups" => return describe_db_subnet_groups(&body),
        "DeleteDBSubnetGroup" => return xml_response(action, ""),
        "DescribeGlobalClusters" => return xml_response(action, "<GlobalClusters/>"),
        "ListTagsForResource" => return xml_response(action, "<TagList/>"),
        "AddTagsToResource" | "RemoveTagsFromResource" => return xml_response(action, ""),
        _ => return aws_error_response(
            StatusCode::NOT_IMPLEMENTED,
            "NotImplemented",
            &format!("{action} is not yet implemented"),
        ),
    }
    .unwrap_or_else(|e| e.into_response())
}

fn create_db_parameter_group(body: &Value) -> Response {
    let group_name = body
        .get("DBParameterGroupName")
        .and_then(|v| v.as_str())
        .unwrap_or("default");
    let family = body
        .get("DBParameterGroupFamily")
        .and_then(|v| v.as_str())
        .unwrap_or("aurora-postgresql16");
    let description = body
        .get("Description")
        .and_then(|v| v.as_str())
        .unwrap_or("");

    let result = format!(
        "<DBParameterGroup>\
           <DBParameterGroupName>{group_name}</DBParameterGroupName>\
           <DBParameterGroupFamily>{family}</DBParameterGroupFamily>\
           <Description>{description}</Description>\
           <DBParameterGroupArn>arn:aws:rds:us-east-1:000000000000:pg:{group_name}</DBParameterGroupArn>\
         </DBParameterGroup>"
    );
    xml_response("CreateDBParameterGroup", &result)
}

fn create_db_cluster_parameter_group(body: &Value) -> Response {
    let group_name = body
        .get("DBClusterParameterGroupName")
        .and_then(|v| v.as_str())
        .unwrap_or("default");
    let family = body
        .get("DBParameterGroupFamily")
        .and_then(|v| v.as_str())
        .unwrap_or("aurora-postgresql16");
    let description = body
        .get("Description")
        .and_then(|v| v.as_str())
        .unwrap_or("");

    let result = format!(
        "<DBClusterParameterGroup>\
           <DBClusterParameterGroupName>{group_name}</DBClusterParameterGroupName>\
           <DBParameterGroupFamily>{family}</DBParameterGroupFamily>\
           <Description>{description}</Description>\
           <DBClusterParameterGroupArn>arn:aws:rds:us-east-1:000000000000:cpg:{group_name}</DBClusterParameterGroupArn>\
         </DBClusterParameterGroup>"
    );
    xml_response("CreateDBClusterParameterGroup", &result)
}

/// Infer the DB parameter group family from a group name like
/// "default.postgres16" or "default.aurora-postgresql16".  Falls back
/// to a sensible default when the name does not follow the
/// `default.<family>` convention.
fn infer_family_from_name(name: &str) -> &str {
    if let Some(suffix) = name.strip_prefix("default.") {
        if !suffix.is_empty() {
            return suffix.rsplit_once('.').map_or(suffix, |(_, s)| s);
        }
    }
    "aurora-postgresql16"
}

fn describe_db_parameter_groups(body: &Value) -> Response {
    if let Some(name) = body.get("DBParameterGroupName").and_then(|v| v.as_str()) {
        let family = infer_family_from_name(name);
        let result = format!(
            "<DBParameterGroups>\
               <DBParameterGroup>\
                 <DBParameterGroupName>{name}</DBParameterGroupName>\
                 <DBParameterGroupFamily>{family}</DBParameterGroupFamily>\
                 <Description>Default parameter group for {family}</Description>\
                 <DBParameterGroupArn>arn:aws:rds:us-east-1:000000000000:pg:{name}</DBParameterGroupArn>\
               </DBParameterGroup>\
             </DBParameterGroups>"
        );
        xml_response("DescribeDBParameterGroups", &result)
    } else {
        // No specific group requested — return a default entry so that
        // providers that enumerate parameter groups get at least one.
        let result =
            "<DBParameterGroups>\
               <DBParameterGroup>\
                 <DBParameterGroupName>default.aurora-postgresql16</DBParameterGroupName>\
                 <DBParameterGroupFamily>aurora-postgresql16</DBParameterGroupFamily>\
                 <Description>Default parameter group for aurora-postgresql16</Description>\
                 <DBParameterGroupArn>arn:aws:rds:us-east-1:000000000000:pg:default.aurora-postgresql16</DBParameterGroupArn>\
               </DBParameterGroup>\
             </DBParameterGroups>";
        xml_response("DescribeDBParameterGroups", result)
    }
}

fn create_db_subnet_group(body: &Value) -> Response {
    let group_name = body
        .get("DBSubnetGroupName")
        .and_then(|v| v.as_str())
        .unwrap_or("default");
    let description = body
        .get("DBSubnetGroupDescription")
        .and_then(|v| v.as_str())
        .unwrap_or("");
    // Collect SubnetIds.member.N from form params
    let mut subnet_ids = Vec::new();
    for i in 1..=20 {
        let key = format!("SubnetIds.member.{i}");
        if let Some(id) = body.get(&key).and_then(|v| v.as_str()) {
            subnet_ids.push(id.to_string());
        } else {
            break;
        }
    }
    let subnets_xml: String = subnet_ids.iter().map(|id| {
        format!(
            "<Subnet><SubnetIdentifier>{id}</SubnetIdentifier>\
             <SubnetAvailabilityZone><Name>us-east-1a</Name></SubnetAvailabilityZone>\
             <SubnetStatus>Active</SubnetStatus></Subnet>"
        )
    }).collect();
    let arn = format!("arn:aws:rds:us-east-1:000000000000:subgrp:{group_name}");
    let result = format!(
        "<DBSubnetGroup>\
           <DBSubnetGroupName>{group_name}</DBSubnetGroupName>\
           <DBSubnetGroupDescription>{description}</DBSubnetGroupDescription>\
           <VpcId>vpc-00000000</VpcId>\
           <SubnetGroupStatus>Complete</SubnetGroupStatus>\
           <Subnets>{subnets_xml}</Subnets>\
           <DBSubnetGroupArn>{arn}</DBSubnetGroupArn>\
           <SupportedNetworkTypes><member>IPV4</member></SupportedNetworkTypes>\
         </DBSubnetGroup>"
    );
    xml_response("CreateDBSubnetGroup", &result)
}

fn describe_db_subnet_groups(body: &Value) -> Response {
    let group_name = body
        .get("DBSubnetGroupName")
        .and_then(|v| v.as_str())
        .unwrap_or("default");
    let arn = format!("arn:aws:rds:us-east-1:000000000000:subgrp:{group_name}");
    let result = format!(
        "<DBSubnetGroups>\
           <DBSubnetGroup>\
             <DBSubnetGroupName>{group_name}</DBSubnetGroupName>\
             <DBSubnetGroupDescription>Subnet group</DBSubnetGroupDescription>\
             <VpcId>vpc-00000000</VpcId>\
             <SubnetGroupStatus>Complete</SubnetGroupStatus>\
             <Subnets/>\
             <DBSubnetGroupArn>{arn}</DBSubnetGroupArn>\
             <SupportedNetworkTypes><member>IPV4</member></SupportedNetworkTypes>\
           </DBSubnetGroup>\
         </DBSubnetGroups>"
    );
    xml_response("DescribeDBSubnetGroups", &result)
}

fn describe_db_cluster_parameter_groups(body: &Value) -> Response {
    if let Some(name) = body.get("DBClusterParameterGroupName").and_then(|v| v.as_str()) {
        let family = infer_family_from_name(name);
        let result = format!(
            "<DBClusterParameterGroups>\
               <DBClusterParameterGroup>\
                 <DBClusterParameterGroupName>{name}</DBClusterParameterGroupName>\
                 <DBParameterGroupFamily>{family}</DBParameterGroupFamily>\
                 <Description>Default parameter group for {family}</Description>\
                 <DBClusterParameterGroupArn>arn:aws:rds:us-east-1:000000000000:cpg:{name}</DBClusterParameterGroupArn>\
               </DBClusterParameterGroup>\
             </DBClusterParameterGroups>"
        );
        xml_response("DescribeDBClusterParameterGroups", &result)
    } else {
        let result =
            "<DBClusterParameterGroups>\
               <DBClusterParameterGroup>\
                 <DBClusterParameterGroupName>default.aurora-postgresql16</DBClusterParameterGroupName>\
                 <DBParameterGroupFamily>aurora-postgresql16</DBParameterGroupFamily>\
                 <Description>Default parameter group for aurora-postgresql16</Description>\
                 <DBClusterParameterGroupArn>arn:aws:rds:us-east-1:000000000000:cpg:default.aurora-postgresql16</DBClusterParameterGroupArn>\
               </DBClusterParameterGroup>\
             </DBClusterParameterGroups>";
        xml_response("DescribeDBClusterParameterGroups", result)
    }
}
