//! PoC: Approach A — Interactive Get for harden flow
//!
//! Validates the full harden cycle:
//!   1. get WITH disable_user_interaction → FAIL (ACL mismatch after rebuild)
//!   2. get WITHOUT disable_user_interaction → SUCCESS (macOS dialog, 1 per key)
//!   3. delete → SUCCESS (no pw_data needed)
//!   4. set (with fresh ACL) → SUCCESS
//!   5. get WITH disable_user_interaction → SUCCESS (new ACL matches)
//!
//! Usage:
//!   cargo run --example poc_interactive_get
//!
//! Prerequisites:
//!   - At least 1 key registered in lkr
//!   - Binary rebuilt after key registration (cdhash mismatch)

use core_foundation::base::TCFType;
use security_framework::os::macos::keychain::SecKeychain;
use std::ffi::c_void;

unsafe extern "C" {
    fn SecKeychainFindGenericPassword(
        keychain: *const c_void,
        service_name_length: u32,
        service_name: *const u8,
        account_name_length: u32,
        account_name: *const u8,
        password_length: *mut u32,
        password_data: *mut *mut c_void,
        item_ref: *mut *mut c_void,
    ) -> i32;

    fn SecKeychainItemDelete(item_ref: *const c_void) -> i32;

    fn SecKeychainItemCreateFromContent(
        item_class: u32,
        attr_list: *const SecKeychainAttributeList,
        length: u32,
        data: *const c_void,
        keychain: *const c_void,
        access: *const c_void,
        item_ref: *mut *mut c_void,
    ) -> i32;

    fn SecTrustedApplicationCreateFromPath(path: *const i8, app_out: *mut *mut c_void) -> i32;

    fn SecAccessCreate(
        descriptor: *const c_void,
        trusted_list: *const c_void,
        access_out: *mut *mut c_void,
    ) -> i32;

    fn CFArrayCreateMutable(
        allocator: *const c_void,
        capacity: isize,
        callbacks: *const c_void,
    ) -> *mut c_void;
    fn CFArrayAppendValue(array: *mut c_void, value: *const c_void);
    fn CFRelease(cf: *const c_void);
    fn CFStringCreateWithBytes(
        alloc: *const c_void,
        bytes: *const u8,
        num_bytes: isize,
        encoding: u32,
        is_external: bool,
    ) -> *mut c_void;
    fn SecKeychainItemFreeContent(attr_list: *const c_void, data: *mut c_void) -> i32;
    fn SecKeychainSetUserInteractionAllowed(state: u8) -> i32;
    static kCFTypeArrayCallBacks: c_void;
}

const SERVICE: &str = "com.llm-key-ring";
const K_CF_STRING_ENCODING_UTF8: u32 = 0x0800_0100;

// CSSM attribute tags for generic password
const CSSM_ATTRIBUTE_TAG_SERVICE: u32 = 7; // kSecServiceItemAttr
const CSSM_ATTRIBUTE_TAG_ACCOUNT: u32 = 11; // kSecAccountItemAttr
const SEC_GENERIC_PASSWORD_ITEM_CLASS: u32 = 0x67656E70; // 'genp'

#[repr(C)]
struct SecKeychainAttribute {
    tag: u32,
    length: u32,
    data: *mut c_void,
}

#[repr(C)]
struct SecKeychainAttributeList {
    count: u32,
    attr: *mut SecKeychainAttribute,
}

fn main() {
    println!("=== PoC: Approach A — Interactive Get for harden flow ===\n");

    // --- Setup: open & unlock keychain ---
    let kc_path = {
        let home = std::env::var("HOME").expect("$HOME not set");
        format!("{}/Library/Keychains/lkr.keychain-db", home)
    };
    println!("[Setup] Opening keychain: {}", kc_path);

    let mut kc = SecKeychain::open(&kc_path).expect("Failed to open keychain");

    eprint!("Keychain password: ");
    let mut password = String::new();
    std::io::stdin()
        .read_line(&mut password)
        .expect("Failed to read password");
    let password = password.trim_end();
    kc.unlock(Some(password))
        .expect("Failed to unlock keychain");
    println!("    Unlocked.\n");

    // Get target account from args or use default
    let target_account = std::env::args()
        .nth(1)
        .unwrap_or_else(|| "poc-test-key".to_string());
    println!("[Target] Account: {}\n", target_account);

    let svc = SERVICE.as_bytes();
    let acct = target_account.as_bytes();

    // ========================================
    // Step 1: get WITH disable_user_interaction → expect FAIL
    // ========================================
    println!("[Step 1] get WITH disable_user_interaction (expect FAIL)...");
    {
        // Disable user interaction
        unsafe {
            SecKeychainSetUserInteractionAllowed(0);
        }

        let mut pw_len: u32 = 0;
        let mut pw_data: *mut c_void = std::ptr::null_mut();

        let status = unsafe {
            SecKeychainFindGenericPassword(
                kc.as_concrete_TypeRef() as _,
                svc.len() as u32,
                svc.as_ptr(),
                acct.len() as u32,
                acct.as_ptr(),
                &mut pw_len,
                &mut pw_data,
                std::ptr::null_mut(),
            )
        };

        // Re-enable user interaction
        unsafe {
            SecKeychainSetUserInteractionAllowed(1);
        }

        if !pw_data.is_null() {
            unsafe {
                SecKeychainItemFreeContent(std::ptr::null(), pw_data);
            }
        }

        println!("    Status: {} {}", status, status_desc(status));
        if status != 0 {
            println!("    -> EXPECTED: ACL mismatch blocks non-interactive get");
        } else {
            println!("    -> UNEXPECTED: get succeeded without dialog.");
            println!("       This means ACL is NOT blocking. Harden would work without fix.");
            println!("       (Rebuild the binary to create cdhash mismatch first!)");
            return;
        }
    }

    // ========================================
    // Step 2: get WITHOUT disable_user_interaction → expect SUCCESS with dialog
    // ========================================
    println!("\n[Step 2] get WITHOUT disable_user_interaction (expect macOS dialog)...");
    println!("    >>> A macOS dialog will appear. Click 'Allow' or 'Always Allow'. <<<\n");

    let key_value: Vec<u8>;
    {
        let mut pw_len: u32 = 0;
        let mut pw_data: *mut c_void = std::ptr::null_mut();

        let status = unsafe {
            SecKeychainFindGenericPassword(
                kc.as_concrete_TypeRef() as _,
                svc.len() as u32,
                svc.as_ptr(),
                acct.len() as u32,
                acct.as_ptr(),
                &mut pw_len,
                &mut pw_data,
                std::ptr::null_mut(),
            )
        };

        println!("    Status: {} {}", status, status_desc(status));
        if status != 0 {
            println!("    -> FAILED: interactive get also failed.");
            if status == -128 {
                println!("    -> User clicked 'Deny' in the dialog.");
            }
            println!("    -> Cannot proceed with harden flow.");
            return;
        }

        // Copy value before freeing
        key_value =
            unsafe { std::slice::from_raw_parts(pw_data as *const u8, pw_len as usize).to_vec() };
        unsafe {
            SecKeychainItemFreeContent(std::ptr::null(), pw_data);
        }

        println!("    -> SUCCESS: got {} bytes", key_value.len());
        // Show first 4 chars for verification
        let preview = String::from_utf8_lossy(&key_value);
        let safe_preview: String = preview.chars().take(4).collect();
        println!("    -> Value preview: {}...", safe_preview);
    }

    // ========================================
    // Step 3: delete → expect SUCCESS
    // ========================================
    println!("\n[Step 3] delete (no pw_data needed, expect SUCCESS)...");
    {
        // Find item_ref for delete (no pw_data)
        let mut item_ref: *mut c_void = std::ptr::null_mut();
        let status = unsafe {
            SecKeychainFindGenericPassword(
                kc.as_concrete_TypeRef() as _,
                svc.len() as u32,
                svc.as_ptr(),
                acct.len() as u32,
                acct.as_ptr(),
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                &mut item_ref,
            )
        };

        if status != 0 || item_ref.is_null() {
            println!("    -> FAILED to find item for delete: {}", status);
            return;
        }

        let del_status = unsafe { SecKeychainItemDelete(item_ref) };
        unsafe {
            CFRelease(item_ref as _);
        }

        println!("    Status: {} {}", del_status, status_desc(del_status));
        if del_status != 0 {
            println!("    -> FAILED: delete failed. KEY MAY BE LOST!");
            println!(
                "    -> Recovery: `lkr set {}` to re-register",
                target_account
            );
            return;
        }
        println!("    -> SUCCESS: item deleted");
    }

    // ========================================
    // Step 4: set with fresh ACL → expect SUCCESS
    // ========================================
    println!("\n[Step 4] set with fresh ACL for current binary...");
    {
        let binary_path = std::env::current_exe()
            .and_then(|p| p.canonicalize())
            .expect("Failed to resolve binary path");
        println!("    Binary: {}", binary_path.display());

        let path_cstr =
            std::ffi::CString::new(binary_path.to_str().unwrap()).expect("path contains NUL");

        // Build fresh ACL
        let access = unsafe {
            let mut trusted_app: *mut c_void = std::ptr::null_mut();
            let ta_status =
                SecTrustedApplicationCreateFromPath(path_cstr.as_ptr(), &mut trusted_app);
            assert_eq!(
                ta_status, 0,
                "SecTrustedApplicationCreateFromPath failed: {}",
                ta_status
            );

            let trusted_list =
                CFArrayCreateMutable(std::ptr::null(), 1, &kCFTypeArrayCallBacks as *const c_void);
            CFArrayAppendValue(trusted_list, trusted_app as _);

            let desc = "LKR API Key Access";
            let cf_desc = CFStringCreateWithBytes(
                std::ptr::null(),
                desc.as_ptr(),
                desc.len() as isize,
                K_CF_STRING_ENCODING_UTF8,
                false,
            );

            let mut access: *mut c_void = std::ptr::null_mut();
            let access_status = SecAccessCreate(cf_desc, trusted_list as _, &mut access);

            CFRelease(cf_desc as _);
            CFRelease(trusted_list as _);
            CFRelease(trusted_app as _);

            assert_eq!(
                access_status, 0,
                "SecAccessCreate failed: {}",
                access_status
            );
            access
        };

        // Create item with value and fresh ACL
        let mut svc_bytes = svc.to_vec();
        let mut acct_bytes = acct.to_vec();

        let mut attrs = vec![
            SecKeychainAttribute {
                tag: CSSM_ATTRIBUTE_TAG_SERVICE,
                length: svc_bytes.len() as u32,
                data: svc_bytes.as_mut_ptr() as *mut c_void,
            },
            SecKeychainAttribute {
                tag: CSSM_ATTRIBUTE_TAG_ACCOUNT,
                length: acct_bytes.len() as u32,
                data: acct_bytes.as_mut_ptr() as *mut c_void,
            },
        ];

        let mut attr_list = SecKeychainAttributeList {
            count: attrs.len() as u32,
            attr: attrs.as_mut_ptr(),
        };

        let status = unsafe {
            SecKeychainItemCreateFromContent(
                SEC_GENERIC_PASSWORD_ITEM_CLASS,
                &mut attr_list,
                key_value.len() as u32,
                key_value.as_ptr() as *const c_void,
                kc.as_concrete_TypeRef() as _,
                access,
                std::ptr::null_mut(),
            )
        };

        unsafe {
            CFRelease(access as _);
        }

        println!("    Status: {} {}", status, status_desc(status));
        if status != 0 {
            println!("    -> FAILED: set failed. KEY IS LOST!");
            println!("    -> Value was: {} bytes", key_value.len());
            println!(
                "    -> Recovery: `lkr set {}` to re-register",
                target_account
            );
            return;
        }
        println!("    -> SUCCESS: item created with fresh ACL");
    }

    // ========================================
    // Step 5: get WITH disable_user_interaction → expect SUCCESS (new ACL)
    // ========================================
    println!("\n[Step 5] get WITH disable_user_interaction (expect SUCCESS with new ACL)...");
    {
        unsafe {
            SecKeychainSetUserInteractionAllowed(0);
        }

        let mut pw_len: u32 = 0;
        let mut pw_data: *mut c_void = std::ptr::null_mut();

        let status = unsafe {
            SecKeychainFindGenericPassword(
                kc.as_concrete_TypeRef() as _,
                svc.len() as u32,
                svc.as_ptr(),
                acct.len() as u32,
                acct.as_ptr(),
                &mut pw_len,
                &mut pw_data,
                std::ptr::null_mut(),
            )
        };

        unsafe {
            SecKeychainSetUserInteractionAllowed(1);
        }

        if !pw_data.is_null() {
            // Verify value matches
            let read_val = unsafe {
                std::slice::from_raw_parts(pw_data as *const u8, pw_len as usize).to_vec()
            };
            unsafe {
                SecKeychainItemFreeContent(std::ptr::null(), pw_data);
            }

            println!("    Status: {} {}", status, status_desc(status));
            if read_val == key_value {
                println!("    -> SUCCESS: value matches original!");
            } else {
                println!("    -> WARNING: value read but doesn't match original");
                println!(
                    "       Original: {} bytes, Read: {} bytes",
                    key_value.len(),
                    read_val.len()
                );
            }
        } else {
            println!("    Status: {} {}", status, status_desc(status));
            println!("    -> FAILED: non-interactive get still fails after ACL refresh.");
            println!("    -> This invalidates Approach A.");
        }
    }

    println!("\n=== PoC Complete ===");
    println!("If Steps 1-5 all behaved as expected, Approach A is validated.");
    println!("Verify with: target/release/lkr get {}", target_account);
}

fn status_desc(status: i32) -> &'static str {
    match status {
        0 => "(errSecSuccess)",
        -128 => "(errSecUserCanceled)",
        -25293 => "(errSecAuthFailed)",
        -25308 => "(errSecInteractionNotAllowed)",
        -25299 => "(errSecDuplicateItem)",
        _ => "(unknown)",
    }
}
