// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

use std::{env, fs, process::Command};

use anyhow::{bail, Context, Result};
use camino::Utf8Path;
use serde::Deserialize;

#[derive(Deserialize)]
struct NotarySubmitOutput {
    id: String,
}

pub fn notarize_app(output_folder: &Utf8Path) -> Result<()> {
    if env::var("ANKI_CODESIGN").is_err() {
        return Ok(());
    }
    let zip_file = output_folder.with_extension("zip");
    assert!(
        Command::new("ditto")
            .args([
                "-c",
                "-k",
                "--keepParent",
                output_folder.as_str(),
                zip_file.as_str(),
            ])
            .status()
            .unwrap()
            .success(),
        "zip build"
    );
    let output = Command::new("xcrun")
        .args([
            "notarytool",
            "submit",
            zip_file.as_str(),
            "-f",
            "json",
            "-p",
            "default",
        ])
        .output()
        .expect("notarytool");
    if !output.status.success() {
        panic!(
            "notarytool submit failed: {} {}",
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        )
    }
    let output: NotarySubmitOutput = match serde_json::from_slice(&output.stdout) {
        Ok(out) => out,
        Err(err) => panic!(
            "unable to parse notary output: {err} {} {}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        ),
    };
    let uuid_path = output_folder.with_extension("uuid");
    fs::write(&uuid_path, output.id).expect("write uuid");
    Ok(())
}

#[derive(Deserialize)]
struct NotaryWaitOutput {
    status: String,
}

pub fn wait_then_staple_app(app: &Utf8Path, uuid: String) -> Result<()> {
    let output = Command::new("xcrun")
        .args(["notarytool", "wait", &uuid, "-p", "default", "-f", "json"])
        .output()
        .context("notary wait")?;
    let output: NotaryWaitOutput = serde_json::from_slice(&output.stdout)
        .with_context(|| String::from_utf8_lossy(&output.stderr).to_string())?;
    if output.status != "Accepted" {
        bail!("unexpected status: {}", output.status);
    }

    assert!(
        Command::new("xcrun")
            .args(["stapler", "staple", app.as_str()])
            .status()
            .context("staple")?
            .success(),
        "staple"
    );

    // clean up temporary files
    fs::remove_file(app.with_extension("zip")).context("app.zip")?;
    fs::remove_file(app.with_extension("uuid")).context("app.uuid")?;

    Ok(())
}
