use anyhow::Result;
use image::EncodableLayout;
#[allow(unused_imports)]
use tracing::{error, info, warn};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SearchResultItem {
    name: String,
    code: String,
    has_device: String,
    img_urls: Vec<String>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct FetchResultItem {
    pub imgs: Vec<iced::widget::image::Handle>,
    pub name: String,
    pub model: String,
    pub code: String,
    pub model_id: String,
    pub datasheet: String,
}
/// 访问一次api，得到需要的部分数据
pub async fn search_keyword(
    keyword: String,
    cur_page: u32,
    page_size: u32,
) -> Result<Vec<SearchResultItem>> {
    let mut form_maps = std::collections::HashMap::new();
    form_maps.insert("keyword", keyword);
    let cur_page = format!("{cur_page}");
    let page_size = format!("{page_size}");
    form_maps.insert("curPage", cur_page);
    form_maps.insert("pageSize", page_size);
    let resp = reqwest::Client::new()
        .post("https://pro.lceda.cn/api/eda/product/search")
        .form(&form_maps)
        .send()
        .await?;
    let text = resp.text().await?;
    let j: KeywordSearchRoot = serde_json::from_str(&text)?;
    let mut r = Vec::new();
    for i in j.result.productList.iter() {
        if let Some(has) = i.hasDevice.clone() {
            let mut imgs = Vec::new();
            if let Some(s) = i.image.clone() {
                for img in s.imgs() {
                    imgs.push(img.clone());
                }
            }
            let ii = SearchResultItem {
                name: i.model.clone(),
                code: i.code.clone(),
                has_device: has,
                img_urls: imgs,
            };
            r.push(ii);
        }
    }
    Ok(r)
}
pub async fn download_step(id: String) -> Result<String> {
    let url = format!("https://modules.lceda.cn/qAxj6KHrDKw4blvCG8QJPs7Y/{id}");
    let url = url.as_str();
    let resp = reqwest::get(url).await?;
    let text = resp.text().await?;
    Ok(text)
}
pub async fn fetch_item(item: SearchResultItem) -> Result<FetchResultItem> {
    let h = search_has_device(&item.has_device).await?;
    let mut has_device = String::new();
    let v: serde_json::Value = serde_json::from_str(h.as_str())?;
    let id = v["result"][0]["attributes"]["3D Model"].clone();
    if let serde_json::Value::String(id) = id {
        has_device = id.clone();
    }

    let mut title = String::new();
    let mut footprint = String::new();
    let mut datasheet = String::new();

    if let serde_json::Value::String(t) = v["result"][0]["title"].clone() {
        title = t;
    }
    if let serde_json::Value::String(t) = v["result"][0]["attributes"]["Supplier Footprint"].clone()
    {
        footprint = t;
    }
    if let serde_json::Value::String(t) = v["result"][0]["attributes"]["Datasheet"].clone() {
        datasheet = t;
    }

    let mut step_id = String::new();
    let resp = search_model_id(has_device.as_str()).await?;
    let mut model = String::new();
    let v: serde_json::Value = serde_json::from_str(resp.as_str())?;
    info!("In search_model_id: The v is : {v:#?}");
    let data_str: serde_json::Value = v["result"][0]["dataStr"].clone();

    if let serde_json::Value::String(data_str) = data_str {
        let data_str: DataStr = serde_json::from_str(data_str.as_str())?;
        if let Some(m) = data_str.model {
            step_id = m;
        } else {
            return Err(anyhow::Error::msg("Failed to get model"));
        }
    }

    let mut imgs = Vec::new();
    for img_url in item.img_urls {
        info!("Fetching img url: {}", img_url);
        let img = reqwest::get(img_url).await?.bytes().await?;
        // let img = image::load_from_memory(&img)?;
        let img = iced::widget::image::Handle::from_bytes(img.clone());
        imgs.push(img);
    }
    let rst = FetchResultItem {
        name: item.name.clone(),
        model_id: step_id,
        model: footprint,
        code: item.code,
        imgs,
        datasheet,
    };
    Ok(rst)
}
async fn search_has_device(has_device: &str) -> Result<String> {
    let mut form_maps = std::collections::HashMap::new();
    form_maps.insert("uuids[]", has_device);
    let url = "https://pro.lceda.cn/api/devices/searchByIds";
    let resp = reqwest::Client::new()
        .post(url)
        .form(&form_maps)
        .send()
        .await?;
    let text = resp.text().await?;
    Ok(text)
}

async fn search_model_id(uuid: &str) -> Result<String> {
    let mut form_maps = std::collections::HashMap::new();
    form_maps.insert("uuids[]", uuid);
    form_maps.insert("dataStr", "yes");
    let url = "https://pro.lceda.cn/api/components/searchByIds?forceOnline=1";

    let resp = reqwest::Client::new()
        .post(url)
        .form(&form_maps)
        .send()
        .await?;
    let text = resp.text().await?;
    return Ok(text);
}
#[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
struct KeywordSearchRoot {
    code: u32,
    success: bool,
    result: KeywordSearchResult,
}
#[allow(non_snake_case)]
#[derive(Default, Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
struct KeywordSearchResult {
    total: u32,
    productList: Vec<KeywordSearchListItem>,
}
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
struct KeywordSearchListItem {
    id: u32,
    code: String,
    image: Option<KeywordChipImages>,
    model: String,
    hasDevice: Option<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
struct KeywordChipImages(String);
#[allow(dead_code)]
impl KeywordChipImages {
    pub fn imgs(&self) -> Vec<String> {
        let urls = self.0.clone();
        let list = urls.split("<$>");
        let mut rst = Vec::new();
        for i in list {
            rst.push(i.to_string());
        }
        rst
    }
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct KeywordDevInfo {
    uuid: String,
    description: String,
    title: String,
    images: Vec<String>,
    attributes: KeywordAttributes,
}
#[allow(non_snake_case)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct KeywordAttributes {
    Datasheet: Option<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct DataStr {
    model: Option<String>,
    src: Option<String>,
    _type: Option<String>,
}
