use crate::common::hash::Hash256;
use crate::common::inscriptions::Genesis;
use crate::common::mime::{Extension, Other};
use crate::common::tokens::InscriptionProtocol;
use crate::external::electrs::{ElectrsDataswampBlock, ElectrsInscription, ProtocolKey};
use crate::utils::sha256::IntoHash256;
use serde::{Deserialize, Serialize};
use sha2::Digest;
use std::fmt::{Display, Formatter};

use super::electrs::BlockInfo;

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PreviewerBlockData {
    pub block_info: BlockInfo,
    pub inscriptions: Vec<PreviewerInscriptionData>,
    pub moved_valid_tokens: Vec<Genesis>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct PreviewerInscriptionData {
    pub genesis: Genesis,
    pub content: Vec<u8>,
    pub primary_key: PreviewerInscriptionKey,
}

#[derive(Deserialize, Serialize, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PreviewerInscriptionKey {
    pub number: u64,
    pub content_hash: Hash256,
    pub extension: Extension,
    pub protocol: Option<InscriptionProtocol>,
}

impl PreviewerInscriptionKey {
    pub fn generate_hash(&self) -> Hash256 {
        let mut buffer = Vec::new();

        buffer.extend(self.content_hash.0);
        buffer.extend(serde_json::to_vec(&self.extension).expect("Must generate vec from extension"));
        buffer.extend(
            self.protocol
                .as_ref()
                .map(|protocol| serde_json::to_vec(protocol).expect("Must generate vec from protocol"))
                .unwrap_or_default(),
        );

        buffer.hash_256()
    }

    pub fn generate_path(&self) -> String {
        let hash = self.generate_hash();
        let file_type = self.extension.file_type();
        format!("{file_type}/{hash}")
    }
}

impl Display for PreviewerInscriptionKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let hash = self.content_hash;
        let protocol = self.protocol.clone().map(|p| format!("{p:?}")).unwrap_or_default();
        let extension = self.extension.to_string();
        write!(f, "{hash}-{protocol}-{extension}")
    }
}

impl From<ElectrsDataswampBlock> for PreviewerBlockData {
    fn from(value: ElectrsDataswampBlock) -> Self {
        let inscriptions = value
            .inscriptions
            .iter()
            .map(|x| PreviewerInscriptionData {
                genesis: x.location.genesis,
                content: x.clone().content.unwrap_or_default(),
                primary_key: PreviewerInscriptionKey::from(
                    x.clone(),
                    value
                        .protocol
                        .get(&ProtocolKey {
                            tx_index: x.tx_index,
                            inscription_number: x.number,
                        })
                        .cloned(),
                ),
            })
            .collect();

        Self {
            block_info: value.block_info,
            inscriptions,
            moved_valid_tokens: vec![],
        }
    }
}

impl PreviewerInscriptionKey {
    fn from(value: ElectrsInscription, protocol: Option<InscriptionProtocol>) -> Self {
        let mut hasher = sha2::Sha256::new();
        hasher.update(value.content.as_deref().unwrap_or_default());
        let content_hash = Hash256(hasher.finalize().into());

        let extension = value.content_type.unwrap_or_default().parse().unwrap_or(Extension::Other(Other::Bytes));

        Self {
            content_hash,
            extension,
            protocol,
            number: value.number.0,
        }
    }
}
