use rand::Rng;
use super::checksum;
use super::date::Gender;
use super::IdResult;
static REGIONS: &[u32] = &[
110101, 110102, 110105, 110106, 110107, 110108, 110109, 120101, 120102, 120103, 120104, 120105, 120106, 130102, 130104, 130105, 130107, 130203, 130204, 130205, 130302, 130303, 130304, 130402, 130403,
130502, 130503, 130602, 130604, 130702, 140202, 140203, 140212, 140302, 150102, 150103, 150104, 150105, 150202, 150203, 150204, 150302, 150402,
210102, 210103, 210104, 210105, 210106, 210111, 210112, 210113, 210114, 210202, 210203, 210204,
210211, 210302, 210303, 210304, 210402, 210502, 210602, 210702, 220102, 220103, 220104, 220105, 220202, 220203, 220204, 220302,
230102, 230103, 230104, 230108, 230202, 230203, 230204, 230302, 230303, 230402, 230502, 230602,
230603, 230702, 230802, 230803, 230902, 231002, 310101, 310104, 310105, 310106, 310107, 310109, 310110, 310112, 320102, 320104, 320105, 320106, 320111, 320113, 320114, 320211, 320302, 320311, 320402, 320404,
320502, 320602, 330102, 330103, 330104, 330105, 330106, 330202, 330203, 330205, 330206, 330211, 330302, 330303,
330402, 330502, 340102, 340103, 340104, 340111, 340202, 340203, 340302, 340304, 340402, 340403, 340405, 340406,
340502, 340503, 340602, 340604, 340802, 350102, 350103, 350104, 350105, 350111, 350121, 350128, 350202, 350203, 350205, 350211, 350302,
350303, 350402, 350403, 350502, 360102, 360103, 360104, 360111, 360202, 360203, 360302, 360402, 360502,
370102, 370103, 370104, 370105, 370202, 370203, 370211, 370302, 370303, 370402,
410102, 410103, 410104, 410105, 410106, 410202, 410203, 410204, 410205, 410302, 410303, 410304,
410305, 410402, 410411, 410502, 410503, 410602, 410702, 420102, 420103, 420104, 420105, 420106, 420107, 420111, 420112, 420113, 420202, 420203, 420205,
420302, 420303, 420602, 420802, 430102, 430103, 430104, 430105, 430111, 430202, 430203, 430204, 430211, 430302, 430304, 430402,
430502, 430503, 430602, 430603, 440103, 440104, 440105, 440106, 440111, 440112, 440203, 440204, 440222, 440224, 440229, 440232,
440302, 440303, 440304, 440305, 440402, 440511, 450102, 450103, 450105, 450202, 450203, 450204, 450205, 450302, 450303, 450304, 450305, 450402,
450403, 450502, 510112, 510113, 510121, 510129, 510131, 510302, 510303, 510304, 510402, 510403, 510411,
520102, 520103, 520111, 520112, 520113, 520203, 530102, 530103, 530111, 530112, 540102, 610102, 610103, 610104, 610111, 610112, 610113, 610114, 610202, 610203, 610302, 610303, 610402,
610403, 620102, 620103, 620104, 620105, 620111, 620201, 620302, 620402, 620502,
630102, 630103, 630104, 632221, 632222, 632223, 632224, 640121, 640122, 640202, 650102, 650103, 650104, 650105, 650106, 650107, 650121, 650202, 650203,
];
pub fn generate(opts: &super::GenOptions, rng: &mut impl Rng) -> String {
let gender = Gender::resolve_or_random(opts.gender, rng);
let (year, month, day) = match opts.year {
Some(y) => super::date::rand_date_with_year(rng, y),
None => super::date::rand_date(rng, 1985, 2005),
};
let full_region = REGIONS[rng.gen_range(0..REGIONS.len())];
let seq = match gender {
Gender::Male => rng.gen_range(0..=498u16) * 2 + 1, Gender::Female => rng.gen_range(0..=499u16) * 2, };
let base = format!(
"{:06}{:04}{:02}{:02}{:03}",
full_region, year, month, day, seq
);
let digits: Vec<u8> = base.bytes().map(|b| b - b'0').collect();
let check = checksum::iso7064_mod11_2(&digits);
format!("{}{}", base, check)
}
pub fn validate(code: &str) -> bool {
let upper = code.to_uppercase();
if upper.len() != 18 {
return false;
}
if !upper[..17].chars().all(|c| c.is_ascii_digit()) {
return false;
}
let last = upper.chars().last().unwrap();
if !last.is_ascii_digit() && last != 'X' {
return false;
}
let digits: Vec<u8> = upper[..17].bytes().map(|b| b - b'0').collect();
checksum::iso7064_mod11_2(&digits) == last
}
pub fn parse(code: &str) -> IdResult {
let upper = code.to_uppercase();
let (gender, dob) = if upper.len() == 18 {
let seq_digit = upper.as_bytes()[16] - b'0';
let g = if seq_digit % 2 == 1 {
Some("male".to_string())
} else {
Some("female".to_string())
};
let d = format!("{}-{}-{}", &upper[6..10], &upper[10..12], &upper[12..14]);
(g, Some(d))
} else {
(None, None)
};
IdResult {
country_code: "".to_string(),
code: upper,
gender,
dob,
valid: validate(code),
}
}