#![allow(clippy::result_large_err)]

use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::{
    action::{ActionError, Context},
    action_impl::{spawn, ActionImpl},
    qemu,
};

/// Build a `deb` package.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Deb {
    packages: Option<PathBuf>,
}

impl Deb {
    /// Create a new `Deb` action.
    pub fn new<P: AsRef<Path>>(packages: P) -> Self {
        Self {
            packages: Some(packages.as_ref().to_path_buf()),
        }
    }
}

impl ActionImpl for Deb {
    fn execute(&self, context: &mut Context) -> Result<(), ActionError> {
        let packages = Path::new(qemu::ARTIFACTS_DIR)
            .join(self.packages.clone().unwrap_or(PathBuf::from(".")));
        if !packages.exists() {
            std::fs::create_dir(&packages).map_err(|err| DebError::mkdir(&packages, err))?;
        }
        let shell = format!(
            r#"#!/usr/bin/env bash
set -xeuo pipefail

echo "PATH at start: $PATH"
export PATH="/root/.cargo/bin:$PATH"
export CARGO_HOME=/workspace/deps
export DEBEMAIL=liw@liw.fi
export DEBFULLNAME="Lars Wirzenius"
/bin/env

command -v cargo
command -v rustc

cargo --version
rustc --version

# Get name and version of source package.
name="$(dpkg-parsechangelog -SSource)"
version="$(dpkg-parsechangelog -SVersion)"

# Get upstream version: everything before the last dash.
uv="$(echo "$version" | sed 's/-[^-]*$//')"

# Files that will be created.
arch="$(dpkg --print-architecture)"
orig="../${{name}}_${{uv}}.orig.tar.xz"
deb="../${{name}}_${{version}}_${{arch}}.deb"
changes="../${{name}}_${{version}}_${{arch}}.changes"

# Create "upstream tarball".
git archive HEAD | xz >"$orig"

# Build package.
dpkg-buildpackage -us -uc

# Dump some information to make it easier to visually verify
# everything looks OK. Also, test the package with the lintian tool.

ls -l ..
for x in ../*.deb; do dpkg -c "$x"; done
# FIXME: disabled while this prevents radicle-native-ci deb from being built.
# lintian -i --allow-root --fail-on warning ../*.changes

# Move files to artifacts directory.
mv ../*_* {}
        "#,
            packages.display()
        );

        spawn(context, &["/bin/bash", "-c", &shell])?;

        Ok(())
    }
}

/// Errors from the `deb` action.
#[derive(Debug, thiserror::Error)]
pub enum DebError {
    /// Failed to create the artifacts directory for `deb` packages.
    #[error("could not create artifacts directory {0}")]
    Mkdir(PathBuf, #[source] std::io::Error),
}

impl DebError {
    fn mkdir<P: Into<PathBuf>>(dirname: P, err: std::io::Error) -> Self {
        Self::Mkdir(dirname.into(), err)
    }
}

impl From<DebError> for ActionError {
    fn from(value: DebError) -> Self {
        Self::Deb(value)
    }
}
