use crate::core::{compile::*, instruction::ArcisInstruction, ir::IntermediateRepresentation};
use sha2::{Digest, Sha256};
use std::io::Write;

#[derive(Debug)]
pub struct ArcisCOptions {
    /// The output directory for the compiled circuit binaries.
    pub out_dir: Option<String>,
}

/// Compile a program.
fn compile(ir: IntermediateRepresentation) -> IntermediateRepresentation {
    Compiler::optimize_into_circuitable(ir)
}

pub fn write_ir(
    ir: IntermediateRepresentation,
    circuit_name: &str,
    arcisc_options: &ArcisCOptions,
) -> Option<String> {
    match ir.write_bytes(circuit_name, arcisc_options.out_dir.clone()) {
        Err(e) => {
            panic!("Failed to write IR for '{}': {}", circuit_name, e);
        }
        Ok(s) => Some(s),
    }
}

pub fn compile_and_write(path: &str) -> Result<(), std::io::Error> {
    let ir_path = format!("{}.arcis.ir", path);
    let ir = read_ir(&ir_path)?;
    let compiled_ir = compile(ir);
    let (temp_circuit, metadata, profile) = Compiler::ir_to_async_mpc_circuit(&compiled_ir);
    let compiled_circuit = Compiler::improve_async_mpc_circuit(temp_circuit);
    let circuit = ArcisInstruction {
        circuit: compiled_circuit,
        metadata,
    };
    {
        let profile_path = format!("{}.profile.json", path);
        let mut file = std::fs::File::create(profile_path)?;
        file.write_all(
            serde_json::to_string(&profile)
                .expect("Serialization failed.")
                .as_bytes(),
        )?;
    }

    let circuit_bytes = bincode::serialize(&circuit).expect("Failed to serialize circuit");

    {
        let circuit_path = format!("{}.arcis", path);
        let mut file = std::fs::File::create(&circuit_path)?;
        file.write_all(&circuit_bytes)?;
    }

    {
        let hash_path = format!("{}.hash", path);
        let hash: [u8; 32] = Sha256::digest(&circuit_bytes).into();
        let mut file = std::fs::File::create(&hash_path)?;
        file.write_all(
            serde_json::to_string(&hash)
                .expect("Failed to serialize circuit hash")
                .as_bytes(),
        )?;
    }

    let weight = {
        let profile_info = circuit.profile_info();
        let weight_path = format!("{}.weight", path);
        let mut file = std::fs::File::create(&weight_path)?;
        let json = profile_info.to_json();
        file.write_all(json.as_bytes())?;
        profile_info.weight()
    };

    println!(
        "Built encrypted instruction weighing {:>12} ACUs, from {}. ",
        weight, ir_path,
    );
    Ok(())
}

pub fn read_ir(path: &str) -> Result<IntermediateRepresentation, std::io::Error> {
    let circ = std::fs::read(path)?;
    IntermediateRepresentation::from_bytes(&circ)
}
