// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Propagation decoder.

use crate::trace::propagation::context::PropagationContext;
use base64::prelude::*;

/// Decode context value packed in `sw8` header.
pub fn decode_propagation(header_value: &str) -> crate::Result<PropagationContext> {
    let pieces: Vec<&str> = header_value.split('-').collect();

    if pieces.len() != 8 {
        return Err(crate::Error::DecodePropagation(
            "failed to parse propagation context: it must have 8 properties.",
        ));
    }

    let do_sample = try_parse_sample_status(pieces[0])?;
    let parent_trace_id = b64_encoded_into_string(pieces[1])?;
    let parent_trace_segment_id = b64_encoded_into_string(pieces[2])?;
    let parent_span_id: i32 = try_parse_parent_span_id(pieces[3])?;
    let parent_service = b64_encoded_into_string(pieces[4])?;
    let parent_service_instance = b64_encoded_into_string(pieces[5])?;
    let destination_endpoint = b64_encoded_into_string(pieces[6])?;
    let destination_address = b64_encoded_into_string(pieces[7])?;

    let context = PropagationContext::new(
        do_sample,
        parent_trace_id,
        parent_trace_segment_id,
        parent_span_id,
        parent_service,
        parent_service_instance,
        destination_endpoint,
        destination_address,
    );

    Ok(context)
}

fn try_parse_parent_span_id(id: &str) -> crate::Result<i32> {
    if let Ok(result) = id.parse::<i32>() {
        Ok(result)
    } else {
        Err(crate::Error::DecodePropagation(
            "failed to parse span id from parent.",
        ))
    }
}

fn try_parse_sample_status(status: &str) -> crate::Result<bool> {
    if status == "0" {
        Ok(false)
    } else if status == "1" {
        Ok(true)
    } else {
        Err(crate::Error::DecodePropagation(
            "failed to parse sample status.",
        ))
    }
}

fn b64_encoded_into_string(enc: &str) -> crate::Result<String> {
    if let Ok(result) = BASE64_STANDARD.decode(enc) {
        if let Ok(decoded_str) = String::from_utf8(result) {
            return Ok(decoded_str);
        }
    }

    Err(crate::Error::DecodePropagation("failed to decode value."))
}
