use super::create::IdentityCreateError;
use super::Complete;
use crate::config;
use libpijul::key::{PublicKey, SecretKey};
use std::fs;
use std::io::{Read, Write};
use std::path::PathBuf;
use anyhow::{bail, Context};
use log::debug;
use thiserror::Error;
const FIRST_IDENTITY_MESSAGE: &str = "It doesn't look like you have any identities configured!
Each author in Pijul is identified by a unique key to provide greater security & flexibility over names/emails.
To make sure humans (including you!) can easily identify these keys, we need a few personal details.
For more information see https://pijul.org/manual/keys.html";
const MIGRATE_IDENTITY_MESSAGE: &str =
    "It seems you have configured an identity in an older version of Pijul, which uses an older identity format!
Please take a moment to confirm your details are correct.";
const MISMATCHED_KEYS_MESSAGE: &str = "It seems the keys on your system are mismatched!
This is most likely the result of data corruption, please check your drive and try again.";
#[derive(Error, Debug)]
pub enum IdentityParseError {
    #[error("Mismatching keys")]
    MismatchingKeys,
    #[error("Could not find secret key at path {0}")]
    NoSecretKey(PathBuf),
    #[error(transparent)]
    Other(#[from] anyhow::Error),
}
pub async fn fix_identities(no_prompt: bool) -> Result<(), anyhow::Error> {
    let mut dir = config::global_config_dir().unwrap();
    dir.push("identities");
    std::fs::create_dir_all(&dir)?;
    dir.pop();
    let identities = Complete::load_all()?;
    if identities.is_empty() {
                        let extraction_result = Complete::from_old_format();
        let mut stderr = std::io::stderr();
        match extraction_result {
            Ok(old_identity) => {
                                writeln!(stderr, "{MIGRATE_IDENTITY_MESSAGE}")?;
                                                if let Err(e) = old_identity.clone().create(no_prompt, true).await {
                    match e {
                        IdentityCreateError::ProveFailed(name) => writeln!(stderr, "Failed to prove identity. You will still be able to create & sign patches, but until you run `pijul identity prove --name {name}` they will not be linked to your personal details. If you are on an enterprise network, perhaps try running with `--no-cert-check`")?,
                        IdentityCreateError::Other(err) => return Err(err),
                    }
                };
                                let identity_path = format!("identities/{}", &old_identity.public_key.key);
                                let paths_to_delete =
                    vec!["publickey.json", "secretkey.json", identity_path.as_str()];
                for path in paths_to_delete {
                    let file_path = dir.join(path);
                    if file_path.exists() {
                        debug!("Deleting old file: {file_path:?}");
                        fs::remove_file(file_path)?;
                    } else {
                        debug!("Could not delete old file (path not found): {file_path:?}");
                    }
                }
            }
            Err(e) => {
                match e {
                    IdentityParseError::MismatchingKeys => {
                        bail!("User must repair broken keys before continuing");
                    }
                    IdentityParseError::NoSecretKey(_) => {
                                                if no_prompt {
                            bail!("No identities configured");
                        }
                        writeln!(stderr, "{FIRST_IDENTITY_MESSAGE}")?;
                        Complete::default()?.create(no_prompt, true).await?;
                    }
                    IdentityParseError::Other(err) => {
                        bail!(err);
                    }
                }
            }
        }
    }
        for identity in Complete::load_all()? {
        identity.valid_keys()?;
    }
    Ok(())
}
impl Complete {
        fn valid_keys(&self) -> Result<bool, anyhow::Error> {
        let public_key = &self.public_key;
        let decryped_public_key = self.decrypt()?.0.public_key();
        if public_key.key != decryped_public_key.key {
            let mut stderr = std::io::stderr();
            writeln!(stderr, "{MISMATCHED_KEYS_MESSAGE}")?;
            writeln!(stderr, "Got the following public key signatures:")?;
            writeln!(stderr, "Plaintext public key: {public_key:#?}")?;
            writeln!(stderr, "Decrypted public key: {decryped_public_key:#?}")?;
            return Ok(false);
        }
        Ok(true)
    }
                                                                                                                                                                                                fn from_old_format() -> Result<Self, IdentityParseError> {
        let config_dir = config::global_config_dir().unwrap();
        let config_path = config_dir.join("config.toml");
        let identities_path = config_dir.join("identities");
        let public_key_path = config_dir.join("publickey.json");
        let secret_key_path = config_dir.join("secretkey.json");
                        if !secret_key_path.exists() {
            return Err(IdentityParseError::NoSecretKey(secret_key_path));
        }
                        
                let mut secret_key_file =
            fs::File::open(&secret_key_path).context("Failed to open secret key file")?;
        let mut secret_key_text = String::new();
        secret_key_file
            .read_to_string(&mut secret_key_text)
            .context("Failed to read secret key file")?;
        let secret_key: SecretKey =
            serde_json::from_str(&secret_key_text).context("Failed to parse secret key file")?;
                        let public_key: PublicKey = if public_key_path.exists() {
            let mut public_key_file =
                fs::File::open(&public_key_path).context("Failed to open public key file")?;
            let mut public_key_text = String::new();
            public_key_file
                .read_to_string(&mut public_key_text)
                .context("Failed to read public key file")?;
            serde_json::from_str(&public_key_text).context("Failed to parse public key file")?
        } else {
            return Err(IdentityParseError::Other(anyhow::anyhow!(
                "Public key does not exist!"
            )));
        };
                let identity: Option<Complete> = if identities_path.exists() {
            if identities_path.is_dir() {
                let identities_iter =
                    fs::read_dir(identities_path).context("Failed to read identities directory")?;
                let mut identities: Vec<Complete> = vec![];
                                for dir_entry in identities_iter {
                    let path = dir_entry.unwrap().path();
                    if path.is_file() {
                                                                        let mut identity_file =
                            fs::File::open(&path).context("Failed to open identity file")?;
                        let mut identity_text = String::new();
                        identity_file
                            .read_to_string(&mut identity_text)
                            .context("Failed to read identity file")?;
                        let deserialization_result: Result<Complete, _> =
                            serde_json::from_str(&identity_text);
                        if deserialization_result.is_ok() {
                            identities.push(
                                deserialization_result
                                    .context("Failed to deserialize identity file")?,
                            );
                        }
                    }
                }
                if identities.len() == 1 {
                    Some(identities[0].clone())
                } else {
                    None
                }
            } else {
                None
            }
        } else {
            None
        };
        let config: super::Config = if config_path.exists() {
            let mut config_file =
                fs::File::open(&config_path).context("Failed to open config file")?;
            let mut config_text = String::new();
            config_file
                .read_to_string(&mut config_text)
                .context("Failed to read config file")?;
            let config_data: config::Global =
                toml::from_str(&config_text).context("Failed to parse config file")?;
            super::Config {
                key_path: config_data.author.key_path.clone(),
                author: config_data.author,
            }
        } else {
            let mut author = config::Author::default();
            author.username = identity
                .as_ref()
                .map_or_else(String::new, |x| x.config.author.username.clone());
            super::Config {
                key_path: None,
                author,
            }
        };
        let identity = Self::new(
            String::from("default"),
            config,
            public_key,
            Some(super::Credentials::from(secret_key)),
        );
        if identity.valid_keys()? {
            Ok(identity)
        } else {
            Err(IdentityParseError::MismatchingKeys)
        }
    }
}