use trazaeo::adaptors::{
    DeterministicS3StorageAdaptor, ProofLogAdaptor, S3PutRequest, SolanaProofLogAdaptor,
    StorageAdaptor,
};
use trazaeo::bootstrap::{
    compute_source_root_hash, validate_source_manifest, SourceFileEntry, SourceManifest,
};
use trazaeo::capture::{build_capture_envelope, CaptureSessionInput, CapturedSegment};
use trazaeo::checkpoint::{
    build_checkpoint_inclusion_proof_for_artifact, build_checkpoint_manifest,
    validate_checkpoint_manifest, verify_checkpoint_inclusion, CheckpointArtifact,
};
use trazaeo::envelope::Attestation;
use trazaeo::solana::{init_solana_client, SolanaConfig};
use trazaeo::utils::Hash;

fn sample_attestation() -> Attestation {
    Attestation {
        signer_id: "station-signer".to_string(),
        key_id: "key-1".to_string(),
        signature: "sig".to_string(),
        signed_at: "2026-01-01T00:00:00Z".to_string(),
    }
}

fn sample_capture_input() -> CaptureSessionInput {
    CaptureSessionInput {
        schema_version: "1.0.0".to_string(),
        issued_at: "2026-01-01T00:00:00Z".to_string(),
        subject_id: "capture-session-1".to_string(),
        capture_role: "transport".to_string(),
        capture_actor_id: "station-a".to_string(),
        capture_system_id: "rx-1".to_string(),
        capture_window: "2026-01-01T00:00:00Z/2026-01-01T00:05:00Z".to_string(),
        input_refs: vec!["station://feed".to_string()],
        output_refs: vec!["obj://raw/1".to_string()],
        content_descriptor_ref: None,
        content_descriptor_hash: None,
        key_id: "key-1".to_string(),
        policy_profile_id: Some("policy-1".to_string()),
    }
}

fn sample_publish_envelope() -> trazaeo::envelope::PublishEnvelope {
    trazaeo::envelope::PublishEnvelope {
        schema_version: "1.0.0".to_string(),
        envelope_type: "publish".to_string(),
        issued_at: "2026-01-01T00:00:00Z".to_string(),
        subject_id: "publish-1".to_string(),
        dataset_id: "sst".to_string(),
        dataset_version: "v1".to_string(),
        input_refs: vec!["obj://in".to_string()],
        output_refs: vec!["obj://out".to_string()],
        published_artifacts: vec![trazaeo::checkpoint::CheckpointArtifact {
            artifact_id: "artifact-1".to_string(),
            content_root_hash: "0000000000000000000000000000000000000000000000000000000000000000"
                .to_string(),
            content_descriptor_ref: None,
            content_descriptor_hash: None,
            media_type: "application/vnd+zarr".to_string(),
        }],
        primary_artifact_id: "artifact-1".to_string(),
        checkpoint_manifest_ref: "checkpoint://1".to_string(),
        checkpoint_manifest_hash:
            "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
        checkpoint_id: "checkpoint-1".to_string(),
        checkpoint_log_root_hash:
            "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
        lineage_refs: vec!["capture://1".to_string()],
        verification_policy_id: "verify-default".to_string(),
        attestations: vec![sample_attestation()],
        key_id: "key-1".to_string(),
        stac_refs: vec![],
        reward_context_ref: None,
        reward_context_hash: None,
        provenance_start_mode: "transport_capture".to_string(),
        bootstrap_origin_label: None,
        reward_eligible: false,
    }
}

#[test]
fn bootstrap_manifest_public_api_validates() {
    let source_files = vec![
        SourceFileEntry {
            source_uri: "s3://bucket/b.nc".to_string(),
            content_hash: "h2".to_string(),
            byte_length: 2,
            observed_mtime: Some("2026-01-02T00:00:00Z".to_string()),
        },
        SourceFileEntry {
            source_uri: "s3://bucket/a.nc".to_string(),
            content_hash: "h1".to_string(),
            byte_length: 1,
            observed_mtime: Some("2026-01-01T00:00:00Z".to_string()),
        },
    ];
    let manifest = SourceManifest {
        manifest_id: "manifest-1".to_string(),
        manifest_created_at: "2026-01-01T00:00:00Z".to_string(),
        source_dataset_id: "dataset-1".to_string(),
        source_file_count: source_files.len(),
        source_root_hash: hex::encode(compute_source_root_hash(&source_files).0),
        source_files,
    };

    validate_source_manifest(&manifest).expect("source manifest validates");
}

#[test]
fn checkpoint_public_api_builds_and_verifies_inclusion() {
    let manifest = build_checkpoint_manifest(
        "checkpoint-1",
        "2026-01-01T00:00:00Z/2026-01-01T01:00:00Z",
        None,
        vec!["sig-1".to_string()],
        vec![
            CheckpointArtifact {
                artifact_id: "artifact-1".to_string(),
                content_root_hash: "root-1".to_string(),
                content_descriptor_ref: None,
                content_descriptor_hash: None,
                media_type: "application/octet-stream".to_string(),
            },
            CheckpointArtifact {
                artifact_id: "artifact-2".to_string(),
                content_root_hash: "root-2".to_string(),
                content_descriptor_ref: None,
                content_descriptor_hash: None,
                media_type: "application/octet-stream".to_string(),
            },
        ],
        vec!["capture://1".to_string()],
    );
    validate_checkpoint_manifest(&manifest).expect("checkpoint manifest validates");

    let proof = build_checkpoint_inclusion_proof_for_artifact(&manifest, "artifact-2")
        .expect("inclusion proof");
    let root = Hash(
        hex::decode(&manifest.checkpoint_log_root_hash)
            .expect("log root hex")
            .try_into()
            .expect("fixed root bytes"),
    );

    assert!(verify_checkpoint_inclusion(&root, &proof));
}

#[test]
fn capture_public_api_builds_valid_envelope() {
    let messages = vec![
        CapturedSegment {
            segment_id: "seg-1".to_string(),
            payload: b"abc".to_vec(),
        },
        CapturedSegment {
            segment_id: "seg-2".to_string(),
            payload: b"defgh".to_vec(),
        },
    ];

    let envelope = build_capture_envelope(&sample_capture_input(), &messages, sample_attestation());
    assert_eq!(envelope.leaf_count, 2);
    assert_eq!(envelope.chunk_size, 5);
    assert!(envelope.validate().is_ok());
}

#[test]
fn storage_adaptor_public_api_returns_deterministic_uri() {
    let adaptor = DeterministicS3StorageAdaptor::new("bucket-a", Some("photos"));
    let stored = adaptor
        .put_bytes(&S3PutRequest {
            key: adaptor.build_key("artifact.bin"),
            bytes: b"hello".to_vec(),
            content_type: "application/octet-stream".to_string(),
        })
        .expect("store object");

    assert_eq!(stored.uri, "s3://bucket-a/photos/artifact.bin");
    assert_eq!(stored.byte_length, 5);
}

#[test]
fn proof_log_adaptor_public_api_logs_verifies_and_queries() {
    let client = init_solana_client(&SolanaConfig {
        cluster: "solana-testnet".to_string(),
        program_id: "program-1".to_string(),
    });
    let adaptor = SolanaProofLogAdaptor::new(client, [7u8; 32], "attestor-key");
    let env = sample_publish_envelope();

    let result = adaptor
        .log_publish_proof(&env, "2026-01-01T00:01:00Z", 1_700_000_000, [0u8; 32])
        .expect("log proof");
    adaptor
        .verify_publish_proof(&env, &result.commitment)
        .expect("verify proof");

    let tx = adaptor
        .get_transaction(&result.commitment.entry_id)
        .expect("transaction lookup")
        .expect("transaction exists");
    let account = adaptor
        .get_proof_log_account(&tx.anchor_account_pda)
        .expect("account lookup")
        .expect("account exists");

    assert_eq!(tx.program_id, adaptor.program_id());
    assert_eq!(
        hex::encode(account.anchored_envelope_hash),
        result.commitment.envelope_hash
    );
    assert_eq!(
        hex::encode(account.anchored_checkpoint_hash),
        result.commitment.checkpoint_hash
    );
    assert_eq!(
        hex::encode(adaptor.chain_root().expect("chain root").0),
        result.commitment.log_root_hash
    );
}
