use crate::crates::CrateId;
use crate::error::{err, Result};
use crate::load::FromRecord;
use csv::StringRecord;
use serde_derive::Deserialize;
use std::collections::BTreeMap as Map;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Row {
    pub crate_id: CrateId,
    pub badge_type: BadgeType,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum BadgeType {
    #[non_exhaustive]
    Appveyor {
        repository: String,
        project_name: Option<String>,
        branch: Option<String>,
        service: Option<String>,
        id: Option<String>,
    },
    #[non_exhaustive]
    AzureDevops {
        project: String,
        pipeline: String,
        build: Option<String>,
    },
    #[non_exhaustive]
    BitbucketPipelines { repository: String, branch: String },
    #[non_exhaustive]
    CircleCi {
        repository: String,
        branch: Option<String>,
    },
    #[non_exhaustive]
    CirrusCi {
        repository: String,
        branch: Option<String>,
    },
    #[non_exhaustive]
    Codecov {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    #[non_exhaustive]
    Coveralls {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    #[non_exhaustive]
    Gitlab {
        repository: String,
        branch: Option<String>,
        tag: Option<String>,
    },
    #[non_exhaustive]
    IsItMaintainedIssueResolution {
        repository: String,
        service: Option<String>,
    },
    #[non_exhaustive]
    IsItMaintainedOpenIssues {
        repository: String,
        service: Option<String>,
    },
    #[non_exhaustive]
    Maintenance { status: MaintenanceStatus },
    #[non_exhaustive]
    TravisCi {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
        master: Option<String>,
        tld: Option<String>,
    },
    Other {
        badge_type: String,
        attributes: Map<String, String>,
    },
}
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
pub enum MaintenanceStatus {
    ActivelyDeveloped,
    AsIs,
    Deprecated,
    Experimental,
    LookingForMaintainer,
    None,
    PassivelyMaintained,
}
impl FromRecord for Row {
    fn from_record(record: &StringRecord, headers: &StringRecord) -> Result<Self> {
        de(record, headers)
    }
}
fn de(record: &StringRecord, headers: &StringRecord) -> Result<Row> {
    #[derive(Deserialize)]
    #[serde(deny_unknown_fields)]
    struct Record<'a> {
        attributes: &'a str,
        badge_type: &'a str,
        crate_id: CrateId,
    }
    let record: Record = record.deserialize(Some(headers)).map_err(err)?;
    let badge_type = match record.badge_type {
        "appveyor" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                #[serde(alias = "project-name")]
                project_name: Option<String>,
                branch: Option<String>,
                service: Option<String>,
                id: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::Appveyor {
                    repository: attributes.repository,
                    project_name: attributes.project_name,
                    branch: attributes.branch,
                    service: attributes.service,
                    id: attributes.id,
                })
        }
        "azure-devops" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                project: String,
                pipeline: String,
                build: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::AzureDevops {
                    project: attributes.project,
                    pipeline: attributes.pipeline,
                    build: attributes.build,
                })
        }
        "bitbucket-pipelines" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: String,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::BitbucketPipelines {
                    repository: attributes.repository,
                    branch: attributes.branch,
                })
        }
        "circle-ci" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::CircleCi {
                    repository: attributes.repository,
                    branch: attributes.branch,
                })
        }
        "cirrus-ci" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::CirrusCi {
                    repository: attributes.repository,
                    branch: attributes.branch,
                })
        }
        "codecov" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
                service: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::Codecov {
                    repository: attributes.repository,
                    branch: attributes.branch,
                    service: attributes.service,
                })
        }
        "coveralls" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
                service: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::Coveralls {
                    repository: attributes.repository,
                    branch: attributes.branch,
                    service: attributes.service,
                })
        }
        "gitlab" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
                tag: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::Gitlab {
                    repository: attributes.repository,
                    branch: attributes.branch,
                    tag: attributes.tag,
                })
        }
        "is-it-maintained-issue-resolution" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                service: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(
                    |attributes: Attributes| BadgeType::IsItMaintainedIssueResolution {
                        repository: attributes.repository,
                        service: attributes.service,
                    },
                )
        }
        "is-it-maintained-open-issues" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                service: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(
                    |attributes: Attributes| BadgeType::IsItMaintainedOpenIssues {
                        repository: attributes.repository,
                        service: attributes.service,
                    },
                )
        }
        "maintenance" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                status: MaintenanceStatus,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::Maintenance {
                    status: attributes.status,
                })
        }
        "travis-ci" => {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Attributes {
                repository: String,
                branch: Option<String>,
                service: Option<String>,
                master: Option<String>,
                tld: Option<String>,
            }
            serde_json::from_str(record.attributes)
                .ok()
                .map(|attributes: Attributes| BadgeType::TravisCi {
                    repository: attributes.repository,
                    branch: attributes.branch,
                    service: attributes.service,
                    master: attributes.master,
                    tld: attributes.tld,
                })
        }
        _other => None,
    };
    let badge_type = if let Some(badge_type) = badge_type {
        badge_type
    } else {
        BadgeType::Other {
            badge_type: record.badge_type.to_owned(),
            attributes: serde_json::from_str(record.attributes).map_err(err)?,
        }
    };
    Ok(Row {
        badge_type,
        crate_id: record.crate_id,
    })
}