use std::collections::HashMap;
use std::ops::Range;

use itertools::Itertools;

use crate::common::block::BlockNumber;
use crate::common::collections::CollectionId;
use crate::common::hash::Hash256;
use crate::common::inscriptions::{Address, Genesis, InscriptionNumber};
use crate::common::mime;
use crate::common::other::TimestampMs;

use super::electrs::ElectrsDataswampBlock;

#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct CollectionSnapshot {
    pub id: CollectionId,
    pub data: CollectionData,
    pub inscriptions: HashMap<Genesis, CollectionInscription>,
    pub update_reason: String,
}

#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct CollectionData {
    pub name: String,
    pub slug: String,
    pub total_size: u64,
    pub supply: u32,
    pub description: String,
    pub inscriptions: Range<InscriptionNumber>,
    pub collection_owner: Address,
    pub inscription_owners: u32,
    pub links: HashMap<String, String>,
    pub created_at: TimestampMs,
    pub modified_at: TimestampMs,
    pub preview: String,
    pub preview_extension: mime::Extension,
    pub extra: HashMap<String, serde_json::Value>,
}

impl CollectionSnapshot {
    pub fn new(id: CollectionId, name: String, slug: String, update_reason: String) -> Self {
        Self {
            id,
            data: CollectionData {
                name,
                slug,
                total_size: 0,
                supply: 0,
                description: String::new(),
                inscriptions: InscriptionNumber::default()..InscriptionNumber::default(),
                collection_owner: Address(Hash256::default()),
                inscription_owners: 0,
                links: HashMap::new(),
                created_at: TimestampMs::now(),
                modified_at: TimestampMs::now(),
                extra: HashMap::new(),
                preview: String::new(),
                preview_extension: mime::Extension::Other(mime::Other::Bytes),
            },
            inscriptions: HashMap::new(),
            update_reason,
        }
    }

    pub fn insert(&mut self, k: Genesis, v: CollectionInscription) {
        self.inscriptions.insert(k, v);
    }

    pub fn remove(&mut self, k: Genesis) {
        self.inscriptions.remove(&k);
    }
}

#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct CollectionInscription {
    #[serde(default)]
    pub name: String,

    #[serde(default)]
    pub attributes: HashMap<String, String>,
}

/// Block data that content will pull from dataswamp
#[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]
pub struct CollectionsBlockData {
    pub height: BlockNumber,
    pub block_hash: Hash256,
    pub inscriptions: Vec<CollectionsInscriptionData>,
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
pub struct CollectionsInscriptionData {
    pub genesis: Genesis,
    pub number: InscriptionNumber,
}

impl From<ElectrsDataswampBlock> for CollectionsBlockData {
    fn from(val: ElectrsDataswampBlock) -> Self {
        Self {
            height: val.block_info.height,
            block_hash: val.block_info.block_hash,
            inscriptions: val
                .inscriptions
                .into_iter()
                .map(|x| CollectionsInscriptionData {
                    genesis: x.location.genesis,
                    number: x.number,
                })
                .collect_vec(),
        }
    }
}
