use serde::{Serialize, de::DeserializeOwned};

use crate::data_model::common::der_take_tlv;

use super::config::*;
use super::pack::PackTime;
use super::{StorageError, StorageResult};

pub(super) fn repository_view_key(rsync_uri: &str) -> String {
    format!("{REPOSITORY_VIEW_KEY_PREFIX}{rsync_uri}")
}

pub(super) fn repository_view_prefix(rsync_uri_prefix: &str) -> String {
    format!("{REPOSITORY_VIEW_KEY_PREFIX}{rsync_uri_prefix}")
}

pub(super) fn raw_by_hash_key(sha256_hex: &str) -> String {
    format!("{RAW_BY_HASH_KEY_PREFIX}{sha256_hex}")
}

pub(super) fn raw_blob_key(sha256_hex: &str) -> String {
    format!("{RAW_BLOB_KEY_PREFIX}{sha256_hex}")
}

pub(super) fn vcir_key(manifest_rsync_uri: &str) -> String {
    format!("{VCIR_KEY_PREFIX}{manifest_rsync_uri}")
}

pub(super) fn vcir_failed_fetch_reuse_identity_key(manifest_rsync_uri: &str) -> String {
    format!("{VCIR_FAILED_FETCH_REUSE_IDENTITY_KEY_PREFIX}{manifest_rsync_uri}")
}

pub(super) fn manifest_replay_meta_key(manifest_rsync_uri: &str) -> String {
    format!("{MANIFEST_REPLAY_META_KEY_PREFIX}{manifest_rsync_uri}")
}

pub(super) fn roa_cache_projection_key(manifest_rsync_uri: &str) -> String {
    format!("{ROA_CACHE_PROJECTION_KEY_PREFIX}{manifest_rsync_uri}")
}

pub(super) fn publication_point_cache_projection_key(manifest_rsync_uri: &str) -> String {
    format!("{PUBLICATION_POINT_CACHE_PROJECTION_KEY_PREFIX}{manifest_rsync_uri}")
}

pub(super) fn child_certificate_cache_projection_key(cache_key_sha256_hex: &str) -> String {
    format!("{CHILD_CERTIFICATE_CACHE_PROJECTION_KEY_PREFIX}{cache_key_sha256_hex}")
}

pub(super) fn publication_point_cache_projection_key_manifest_uri(key: &[u8]) -> Option<String> {
    let key = std::str::from_utf8(key).ok()?;
    key.strip_prefix(PUBLICATION_POINT_CACHE_PROJECTION_KEY_PREFIX)
        .map(str::to_string)
}

pub(super) fn rrdp_source_key(notify_uri: &str) -> String {
    format!("{RRDP_SOURCE_KEY_PREFIX}{notify_uri}")
}

pub(super) fn rrdp_source_member_key(notify_uri: &str, rsync_uri: &str) -> String {
    format!("{RRDP_SOURCE_MEMBER_KEY_PREFIX}{notify_uri}:{rsync_uri}")
}

pub(super) fn rrdp_source_member_prefix(notify_uri: &str) -> String {
    format!("{RRDP_SOURCE_MEMBER_KEY_PREFIX}{notify_uri}:")
}

pub(super) fn rrdp_uri_owner_key(rsync_uri: &str) -> String {
    format!("{RRDP_URI_OWNER_KEY_PREFIX}{rsync_uri}")
}

pub(super) fn encode_cbor<T: Serialize>(value: &T, entity: &'static str) -> StorageResult<Vec<u8>> {
    serde_cbor::to_vec(value).map_err(|e| StorageError::Codec {
        entity,
        detail: e.to_string(),
    })
}

pub(super) fn decode_cbor<T: DeserializeOwned>(
    bytes: &[u8],
    entity: &'static str,
) -> StorageResult<T> {
    serde_cbor::from_slice(bytes).map_err(|e| StorageError::Codec {
        entity,
        detail: e.to_string(),
    })
}

pub(super) fn validate_non_empty(field: &'static str, value: &str) -> StorageResult<()> {
    if value.is_empty() {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "must not be empty".to_string(),
        });
    }
    Ok(())
}

pub(super) fn validate_sha256_hex(field: &'static str, value: &str) -> StorageResult<()> {
    if value.len() != 64 || !value.as_bytes().iter().all(u8::is_ascii_hexdigit) {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "must be a 64-character lowercase or uppercase SHA-256 hex string".to_string(),
        });
    }
    Ok(())
}

pub(super) fn decode_sha256_hex_32(field: &'static str, value: &str) -> StorageResult<[u8; 32]> {
    validate_sha256_hex(field, value)?;
    let mut out = [0u8; 32];
    hex::decode_to_slice(value, &mut out).map_err(|e| StorageError::InvalidData {
        entity: field,
        detail: format!("hex decode failed: {e}"),
    })?;
    Ok(out)
}

pub(super) fn validate_manifest_number_be(field: &'static str, value: &[u8]) -> StorageResult<()> {
    if value.is_empty() {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "must not be empty".to_string(),
        });
    }
    if value.len() > 20 {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "must be at most 20 octets".to_string(),
        });
    }
    if value.len() > 1 && value[0] == 0 {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "must be minimal big-endian without leading zeros".to_string(),
        });
    }
    Ok(())
}

pub(super) fn validate_sha256_digest_bytes(field: &'static str, value: &[u8]) -> StorageResult<()> {
    if value.len() != 32 {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: format!("must be 32 bytes, got {}", value.len()),
        });
    }
    Ok(())
}

pub(super) fn validate_fixed_len_bytes(
    field: &'static str,
    value: &[u8],
    expected_len: usize,
) -> StorageResult<()> {
    if value.len() != expected_len {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: format!("must be {expected_len} bytes, got {}", value.len()),
        });
    }
    Ok(())
}

pub(super) fn validate_sorted_unique_fixed_len_bytes(
    field: &'static str,
    values: &[Vec<u8>],
    expected_len: usize,
) -> StorageResult<()> {
    for value in values {
        validate_fixed_len_bytes(field, value, expected_len)?;
    }
    for window in values.windows(2) {
        if window[0] >= window[1] {
            return Err(StorageError::InvalidData {
                entity: field,
                detail: "must be strictly sorted and unique".to_string(),
            });
        }
    }
    Ok(())
}

pub(super) fn validate_full_der_with_tag(
    field: &'static str,
    der: &[u8],
    expected_tag: Option<u8>,
) -> StorageResult<()> {
    let (tag, _value, rem) = der_take_tlv(der).map_err(|detail| StorageError::InvalidData {
        entity: field,
        detail,
    })?;
    if !rem.is_empty() {
        return Err(StorageError::InvalidData {
            entity: field,
            detail: "trailing bytes after DER object".to_string(),
        });
    }
    if let Some(expected_tag) = expected_tag {
        if tag != expected_tag {
            return Err(StorageError::InvalidData {
                entity: field,
                detail: format!("unexpected tag 0x{tag:02X}, expected 0x{expected_tag:02X}"),
            });
        }
    }
    Ok(())
}

pub(super) fn parse_time(
    field: &'static str,
    value: &PackTime,
) -> StorageResult<time::OffsetDateTime> {
    value.parse().map_err(|detail| StorageError::InvalidData {
        entity: field,
        detail,
    })
}
