mod common;

use base64ct::{Base64, Encoding};
use fatoora_core::api::ZatcaClient;
use fatoora_core::config::{Config, EnvironmentType};
use fatoora_core::csr::{CsrProperties, SigningKey};
use std::path::Path;
use fatoora_core::csr::Csr;

fn should_skip_network_tests() -> bool {
    match std::env::var("SKIP_ZATCA_LIVE_API") {
        Ok(value) => {
            let value = value.to_ascii_lowercase();
            value == "1" || value == "true" || value == "yes"
        }
        Err(_) => false,
    }
}

#[tokio::test]
async fn post_compliance_csid_sandbox() {
    if should_skip_network_tests() {
        return;
    }
    let otp = "123345";
    let csr_path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/csrs/test_zatca_en1.csr");
    let csr_b64 = std::fs::read_to_string(csr_path).expect("read CSR");
    let csr_der = Base64::decode_vec(csr_b64.trim()).expect("decode CSR base64");
    let csr = Csr::from_der(&csr_der).expect("parse CSR");

    let client = ZatcaClient::new(Config::default()).expect("client builds");

    let response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("CSID request succeeds");
    assert!(
        !response.binary_security_token().is_empty(),
        "empty binary_security_token. response: {:?}",
        response
    );
    assert!(
        !response.secret().is_empty(),
        "empty secret. response: {:?}",
        response
    );
}

#[tokio::test]
async fn post_production_csid_sandbox() {
    if should_skip_network_tests() {
        return;
    }
    let otp = "123345";
    let csr_path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/csrs/test_zatca_en1.csr");
    let csr_b64 = std::fs::read_to_string(csr_path).expect("read CSR");
    let csr_der = Base64::decode_vec(csr_b64.trim()).expect("decode CSR base64");
    let csr = Csr::from_der(&csr_der).expect("parse CSR");
    let client = ZatcaClient::new(Config::default()).expect("client builds");
    let compliance_response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("CSID request succeeds");
    println!("Compliance CSID Response: {:?}", compliance_response);
    let production_response = client
        .post_ccsid_for_pcsid(&compliance_response)
        .await
        .expect("Production CSID request succeeds");
    println!("Production CSID Response: {:?}", production_response);
    assert!(
        !production_response.binary_security_token().is_empty(),
        "empty binary_security_token. response: {:?}",
        production_response
    );
    assert!(
        !production_response.secret().is_empty(),
        "empty secret. response: {:?}",
        production_response
    );
}

#[tokio::test]
async fn check_compliance_with_live_ccsid() {
    if should_skip_network_tests() {
        return;
    }
    let otp = "123345";
    let config_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/csr-configs/csr-config-example-EN.properties");
    let csr_props = std::fs::read_to_string(&config_path).expect("read csr config");
    let csr_config = CsrProperties::from_properties_str(&csr_props).expect("csr config");
    let signer_key = SigningKey::generate();
    let csr = csr_config
        .build(&signer_key, EnvironmentType::NonProduction)
        .expect("csr build");
    let client = ZatcaClient::new(Config::default()).expect("client");
    let ccsid_response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("csid response");

    let signer = common::signer_from_csid(ccsid_response.binary_security_token(), &signer_key);
    let signed_invoice = common::dummy_finalized_invoice()
        .sign(&signer)
        .expect("sign invoice with live csid");
    let compliance_response = client
        .check_invoice_compliance(&signed_invoice, &ccsid_response)
        .await
        .expect("report signed invoice");
    assert_eq!(
        compliance_response.reporting_status(),
        Some("REPORTED"),
        "invoice not reported successfully"
    );
}

#[tokio::test]
async fn report_invoice_with_live_pcsid() {
    if should_skip_network_tests() {
        return;
    }
    // csr generation
    let config_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/csr-configs/csr-config-example-EN.properties");
    let csr_props = std::fs::read_to_string(&config_path).expect("read csr config");
    let csr_config = CsrProperties::from_properties_str(&csr_props).expect("csr config");

    let key_path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pkeys/test_zatca_pkey.der");
    let zatca_pkey = SigningKey::from_der(&std::fs::read(key_path).unwrap()).unwrap();
    let csr = csr_config
        .build(&zatca_pkey, EnvironmentType::NonProduction)
        .unwrap();

    // now we obtain the ccsids
    let otp = "123345";
    let client = ZatcaClient::new(Config::default()).expect("client");
    let ccsid_response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("csid response");

    let signer = common::signer_from_csid(ccsid_response.binary_security_token(), &zatca_pkey);
    let signed_invoice = common::dummy_finalized_invoice()
        .sign(&signer)
        .expect("sign invoice with live csid");

    let compliance_response = client
        .check_invoice_compliance(&signed_invoice, &ccsid_response)
        .await
        .expect("report signed invoice");
    assert_eq!(
        compliance_response.reporting_status(),
        Some("REPORTED"),
        "Compliance check failed!"
    );
    let pcsid_response = client
        .post_ccsid_for_pcsid(&ccsid_response)
        .await
        .expect("csid response");
    let signer = common::signer_from_csid(pcsid_response.binary_security_token(), &zatca_pkey);
    let signed_invoice = common::dummy_finalized_invoice()
        .sign(&signer)
        .expect("sign invoice with live csid");

    let reporting_response = client
        .report_simplified_invoice(&signed_invoice, &pcsid_response, false, None)
        .await
        .expect("report signed invoice");
    println!(
        "Invoice reporting errors: {:#?}",
        reporting_response.validation_results()
    );
    assert_eq!(
        reporting_response.reporting_status(),
        Some("REPORTED"),
        "invoice not reported successfully"
    );
}

#[tokio::test]
async fn renew_csid_returns_request_id() {
    if should_skip_network_tests() {
        return;
    }
    let otp = "123456";
    let config_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/csr-configs/csr-config-example-EN.properties");
    let csr_props = std::fs::read_to_string(&config_path).expect("read csr config");
    let csr_config = CsrProperties::from_properties_str(&csr_props).expect("csr config");

    let key_path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pkeys/test_zatca_pkey.der");
    let zatca_pkey = SigningKey::from_der(&std::fs::read(key_path).unwrap()).unwrap();
    let csr = csr_config
        .build(&zatca_pkey, EnvironmentType::NonProduction)
        .unwrap();
    let client = ZatcaClient::new(Config::default()).expect("client");

    let ccsid_response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("csid response");
    let pcsid_response = client
        .post_ccsid_for_pcsid(&ccsid_response)
        .await
        .expect("pcsid response");

    let response = client
        .renew_csid(&pcsid_response, &csr, otp, None)
        .await
        .expect("renew csid succeeds");
    assert!(
        response.request_id().is_some(),
        "missing request_id. response: {:?}",
        response
    );
}

#[tokio::test]
async fn clear_invoice_with_live_pcsid() {
    if should_skip_network_tests() {
        return;
    }
    let config_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/csr-configs/csr-config-example-EN.properties");
    let csr_props = std::fs::read_to_string(&config_path).expect("read csr config");
    let csr_config = CsrProperties::from_properties_str(&csr_props).expect("csr config");

    let key_path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/pkeys/test_zatca_pkey.der");
    let zatca_pkey = SigningKey::from_der(&std::fs::read(key_path).unwrap()).unwrap();
    let csr = csr_config
        .build(&zatca_pkey, EnvironmentType::NonProduction)
        .unwrap();

    let otp = "123345";
    let client = ZatcaClient::new(Config::default()).expect("client");
    let ccsid_response = client
        .post_csr_for_ccsid(&csr, otp)
        .await
        .expect("csid response");
    let pcsid_response = client
        .post_ccsid_for_pcsid(&ccsid_response)
        .await
        .expect("pcsid response");

    let signer = common::signer_from_csid(pcsid_response.binary_security_token(), &zatca_pkey);
    let invoice = {
        use fatoora_core::invoice::{
            Address, CountryCode, InvoiceBuilder, InvoiceSubType, InvoiceType, LineItem, OtherId,
            Party, SellerRole, VatCategory,
        };

        let seller = Party::<SellerRole>::new(
            "Acme Inc".into(),
            Address {
                country_code: CountryCode::parse("SAU").expect("country code"),
                city: "Riyadh".into(),
                street: "King Fahd".into(),
                additional_street: None,
                building_number: "1234".into(),
                additional_number: Some("5678".into()),
                postal_code: "12222".into(),
                subdivision: None,
                district: Some("Olaya".into()),
            },
            "399999999900003",
            Some(OtherId::with_scheme("7003339333", "CRN")),
        )
        .expect("valid seller");

        let line_items = vec![LineItem::new(
            "Item",
            1.0,
            "PCE",
            100.0,
            15.0,
            VatCategory::Standard,
        )];

        let mut builder = InvoiceBuilder::new(InvoiceType::Tax(InvoiceSubType::Standard));
        builder
            .set_id("INV-0-1")
            .set_uuid("8e6000cf-1a98-4174-b3e7-b5d5954bc10d")
            .set_issue_datetime("2024-01-01T12:30:00Z")
            .set_currency("SAR")
            .set_previous_invoice_hash(
                "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==",
            )
            .set_invoice_counter(0)
            .set_seller(seller)
            .set_payment_means_code("10")
            .set_vat_category(VatCategory::Standard);
        for item in line_items {
            builder.add_line_item(item);
        }
        builder.build().expect("build invoice")
    };

    let signed_invoice = invoice.sign(&signer).expect("sign invoice with live pcsid");

    let clearance_response = client
        .clear_standard_invoice(&signed_invoice, &pcsid_response, true, None)
        .await
        .expect("clear signed invoice");
    println!(
        "Invoice clearance errors: {:#?}",
        clearance_response.validation_results()
    );
    assert_eq!(
        clearance_response.clearance_status(),
        Some("CLEARED"),
        "invoice not cleared successfully"
    );
}
