#![allow(non_snake_case)]

use std::collections::HashMap;

use objc2::__framework_prelude::*;
use objc2_foundation::{
    NSDate, NSDictionary, NSISO8601DateFormatter, NSLocale, NSString, NSTimeZone, ns_string,
};

#[link(name = "AOSKit", kind = "framework")]
unsafe extern "C" {}
extern_class!(
    #[unsafe(super(NSObject))]
    struct AOSUtilities;
);

impl AOSUtilities {
    extern_methods!(
        #[unsafe(method(machineSerialNumber))]
        #[unsafe(method_family = none)]
        fn machineSerialNumber() -> Retained<NSString>;

        #[unsafe(method(retrieveOTPHeadersForDSID:))]
        #[unsafe(method_family = none)]
        fn retrieveOTPHeadersForDSID(arg1: &NSString)
        -> Retained<NSDictionary<NSString, NSString>>;
    );
}

#[link(name = "AuthKit", kind = "framework")]
unsafe extern "C" {}
extern_class!(
    #[unsafe(super(NSObject))]
    struct AKDevice;
);

impl AKDevice {
    extern_methods!(
        #[unsafe(method(currentDevice))]
        #[unsafe(method_family = none)]
        fn currentDevice() -> Retained<AKDevice>;

        #[unsafe(method(localUserUUID))]
        #[unsafe(method_family = none)]
        fn localUserUUID(&self) -> Retained<NSString>;

        #[unsafe(method(locale))]
        #[unsafe(method_family = none)]
        fn locale(&self) -> Retained<NSLocale>;

        #[unsafe(method(serverFriendlyDescription))]
        #[unsafe(method_family = none)]
        fn serverFriendlyDescription(&self) -> Retained<NSString>;

        #[unsafe(method(uniqueDeviceIdentifier))]
        #[unsafe(method_family = none)]
        fn uniqueDeviceIdentifier(&self) -> Retained<NSString>;
    );
}

pub fn get_authentication_headers() -> Option<HashMap<String, String>> {
    let mut headers_map = HashMap::new();

    let headers = AOSUtilities::retrieveOTPHeadersForDSID(ns_string!("-2"));

    let otp = headers.objectForKey(ns_string!("X-Apple-MD"))?.to_string();
    headers_map.insert("X-Apple-I-MD".to_string(), otp);

    let mid = headers
        .objectForKey(ns_string!("X-Apple-MD-M"))?
        .to_string();
    headers_map.insert("X-Apple-I-MD-M".to_string(), mid);

    let current_device = AKDevice::currentDevice();

    let machine_serial_number = AOSUtilities::machineSerialNumber().to_string();
    headers_map.insert("X-Apple-I-SRL-NO".to_string(), machine_serial_number);

    let local_user_uuid = current_device.localUserUUID().to_string();
    headers_map.insert("X-Apple-I-MD-LU".to_string(), local_user_uuid);

    headers_map.insert("X-Apple-I-MD-RINFO".to_string(), "17106176".to_string());

    let locale = current_device.locale().localeIdentifier().to_string();
    headers_map.insert("X-Apple-Locale".to_string(), locale);

    let server_friendly_description = current_device.serverFriendlyDescription().to_string();
    headers_map.insert("X-Mme-Client-Info".to_string(), server_friendly_description);

    let unique_device_identifier = current_device.uniqueDeviceIdentifier().to_string();
    headers_map.insert("X-Mme-Device-Id".to_string(), unique_device_identifier);

    let current_date = NSDate::now();
    let formatter = NSISO8601DateFormatter::new();
    let date_string = formatter.stringFromDate(&current_date).to_string();
    headers_map.insert("X-Apple-I-Client-Time".to_string(), date_string);

    let system_timezone = NSTimeZone::systemTimeZone();
    let timezone_abbr = system_timezone.abbreviation().map(|s| s.to_string())?;
    headers_map.insert("X-Apple-I-TimeZone".to_string(), timezone_abbr);

    Some(headers_map)
}
