// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024

use std::io::Write;

use anyhow::{Context, Result};
use log::{info, warn};
use pv::misc::{open_file, read_file};
use pv::request::SymKey;
use pvimg::error::{Error, OwnExitCode};
use pvimg::uvdata::{EnvelopeSeHdrV1, KeyExchangeTrait, SeH, SeHdr, UvDataTrait};

use crate::cli::{InfoArgs, OutputFormatKind, OutputFormatSpec, OutputFormatVariant};

pub const INFO_V1_JSON_SCHEMA: &str = include_str!("../../schema/info-v1.schema.json");

pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
    let mut output = std::io::stdout();
    if let Some(format_kind) = opt.print_schema {
        match format_kind {
            OutputFormatKind::Text => {
                writeln!(output, "No schema for {format_kind} format available!")?
            }
            OutputFormatKind::Json => write!(output, "{INFO_V1_JSON_SCHEMA}")?,
        }
        return Ok(OwnExitCode::Success);
    }

    let input_path = opt.input.as_ref().unwrap().path.clone();
    info!("Reading Secure Execution header {}", input_path.display(),);
    let mut img = open_file(&input_path)?;

    SeHdr::seek_sehdr(&mut img, None)?;
    let hdr = SeHdr::try_from_io(&mut img)?;
    let se_hdr = if let Some(key_path) = &opt.hdr_key {
        let key = SymKey::try_from_data(
            hdr.key_type(),
            read_file(key_path, "Reading header protection key")?.into(),
        )
        .map_err(|err| Error::InvalidSeHdrProtectionKey {
            source: Box::new(Error::Pv(err)),
        })?;
        let decrypted_hdr = hdr
            .decrypt(&key)
            .context("Failed to authenticate and decrypt the Secure Execution header")?;
        if opt.show_secrets {
            SeH::DecryptedSeHdr {
                se_hdr: decrypted_hdr,
                verified: true,
            }
        } else {
            SeH::SeHdr {
                se_hdr: hdr,
                verified: true,
            }
        }
    } else {
        warn!("WARNING: The Secure Execution header integrity and authenticity was not verified. Specify '--hdr-key' to authenticate it. Do not trust the data without verification.");
        SeH::SeHdr {
            se_hdr: hdr,
            verified: false,
        }
    };

    match opt.format.unwrap_or_else(OutputFormatSpec::detect) {
        OutputFormatSpec {
            kind: OutputFormatKind::Text,
            variant: OutputFormatVariant::Default,
        } => write!(output, "{se_hdr}").context("Cannot generate the human readable output")?,
        OutputFormatSpec {
            kind: OutputFormatKind::Text,
            variant: OutputFormatVariant::Full,
        } => write!(output, "{se_hdr:#}").context("Cannot generate the human readable output")?,
        OutputFormatSpec {
            kind: OutputFormatKind::Json,
            variant,
        } => {
            let doc = EnvelopeSeHdrV1::new(se_hdr);
            match variant {
                OutputFormatVariant::Minify => {
                    serde_json::to_writer(&mut output, &doc)?;
                }
                OutputFormatVariant::Default | OutputFormatVariant::Pretty => {
                    serde_json::to_writer_pretty(&mut output, &doc)?;
                }
                OutputFormatVariant::Full => {
                    unreachable!("we already validate the variant in the outer match")
                }
            }
            // Make sure the output ends with a new line
            writeln!(&mut output)?
        }
        _ => unreachable!(),
    }
    output.flush()?;

    Ok(OwnExitCode::Success)
}
