//! Comprehensive Labeling Modes and Provenance Scenarios.
//!
//! This example demonstrates the broad set of provenance options available in the
//! tagid redesign and shows how developers should use type aliases to make
//! ID handling clean and accessible.
//!
//! Concepts covered:
//! 1. Type Aliases for semantic clarity.
//! 2. Simplified construction using `.for_labeled()`.
//! 3. LabelPolicy defaults (how IDs look in logs by default).
//! 4. Explicit overrides via `.labeled().mode(...)`.
//! 5. Broad Provenance taxonomy (8 core types).

use tagid::id::provenance::{AliasOf, External, Generated, Imported, Scoped, Temporary, providers};
use tagid::{Entity, Id, IdGenerator, Label, LabelMode, MakeLabeling, Sourced};

// ============================================================================
// 1. Define Entity Types
// ============================================================================

struct User;
impl Label for User {
    type Labeler = MakeLabeling<Self>;
    fn labeler() -> Self::Labeler {
        MakeLabeling::default()
    }
}

// Enable generation for internal Users
struct MockGen;
impl IdGenerator for MockGen {
    type IdType = String;
    fn next_id_rep() -> Self::IdType {
        "018d6f-7b2a".to_string()
    }
}
impl Entity for User {
    type IdGen = MockGen;
}

struct Customer;
impl Label for Customer {
    type Labeler = MakeLabeling<Self>;
    fn labeler() -> Self::Labeler {
        MakeLabeling::default()
    }
}

struct Page;
impl Label for Page {
    type Labeler = MakeLabeling<Self>;
    fn labeler() -> Self::Labeler {
        MakeLabeling::default()
    }
}

// ============================================================================
// 2. Define Type Aliases (The Recommended Practice)
// ============================================================================

/// Internal IDs generated by our system
type UserId = Id<Sourced<User, Generated>, String>;

/// IDs from a foreign system (Stripe) - Opaque strings
type StripeId = Id<Sourced<Customer, External<providers::Stripe>>, String>;

/// IDs brought in during a migration from a legacy database
type LegacyUserId = Id<Sourced<User, Imported>, i64>;

/// Ephemeral IDs for session tracking or optimistic UI
type SessionId = Id<Sourced<User, Temporary>, String>;

/// Secondary identifier (e.g. email or URL slug) referring to the same entity
type PageSlug = Id<Sourced<Page, AliasOf<UserId>>, String>;

/// Context-dependent IDs (e.g. Issue #123 within a specific GitHub Repo)
type RepoIssueId = Id<Sourced<User, Scoped<String, External>>, u32>;

fn main() {
    println!("=== tagid Redesign: Labeling & Provenance Demo ===\n");

    // ------------------------------------------------------------------------
    // CONSTRUCTION: Provenance-Aware Functions
    // ------------------------------------------------------------------------
    // tagid provides semantic construction functions that clarify where an ID
    // comes from. These are zero-cost aliases to `from_canonical()`.
    let user_id = UserId::new(); // Uses Entity::next_id()
    let stripe_id = StripeId::from_source("cus_L3H8Z6".to_string());
    let legacy_id = LegacyUserId::from_source(98765);
    let slug_id = PageSlug::alias_for("welcome-to-tagid".to_string());

    // ------------------------------------------------------------------------
    // DEFAULT OUTPUT (Respects LabelPolicy)
    // ------------------------------------------------------------------------
    // Each provenance defines a policy for what context to show in logs.

    println!("Default Human Output (Policy-driven):");

    // Generated -> EntityNameDefault (Show Entity only)
    println!("  Generated:  {}", user_id.labeled());
    // Output: User::018d6f-7b2a

    // External -> ExternalKeyDefault (Show Entity + Provenance slug/vendor)
    println!("  External:   {}", stripe_id.labeled());
    // Output: Customer@ext/stripe::cus_L3H8Z6

    // Temporary -> OpaqueByDefault (Show value only)
    let session = SessionId::for_temporary("sess_123".to_string());
    println!("  Temporary:  {}", session.labeled());
    // Output: sess_123

    // Alias -> EntityNameDefault
    println!("  Alias:      {}", slug_id.labeled());
    // Output: Page::welcome-to-tagid

    // ------------------------------------------------------------------------
    // EXPLICIT OVERRIDES (The Builder Pattern)
    // ------------------------------------------------------------------------
    // Developers can always opt-in to more or less detail.

    println!("\nExplicit Overrides:");

    // Force "Full" mode even for internal IDs
    println!("      Policy:  {}", user_id.labeled());
    println!("  Force Full:  {}", user_id.labeled().mode(LabelMode::Full));
    println!(
        " Force Short:  {}",
        user_id.labeled().mode(LabelMode::Short)
    );
    println!("  Force None:  {}", user_id.labeled().mode(LabelMode::None));
    // Output: User@generated::018d6f-7b2a

    // Force "Short" mode for external IDs (hiding the provenance part)
    println!();
    println!("      Policy: {}", stripe_id.labeled());
    println!(
        "  Force Full: {}",
        stripe_id.labeled().mode(LabelMode::Full)
    );
    println!(
        " Force Short: {}",
        stripe_id.labeled().mode(LabelMode::Short)
    );
    println!(
        "  Force None:  {}",
        stripe_id.labeled().mode(LabelMode::None)
    );
    // Output: Customer::cus_L3H8Z6

    // Force "None" (Canonical)
    println!();
    println!("      Policy:  {}", legacy_id.labeled());
    println!(
        "  Force Full:  {}",
        legacy_id.labeled().mode(LabelMode::Full)
    );
    println!(
        " Force Short:  {}",
        legacy_id.labeled().mode(LabelMode::Short)
    );
    println!(
        "  Force None:  {}",
        legacy_id.labeled().mode(LabelMode::None)
    );
    // Output: 98765

    // ------------------------------------------------------------------------
    // RECALL: Canonical vs Labeled
    // ------------------------------------------------------------------------
    println!("\nCanonical Form (Always use for DB/API/Serde):");
    println!("  Display:     {}", stripe_id); // cus_L3H8Z6
    println!("  as_str():    {}", stripe_id.as_str()); // cus_L3H8Z6

    // Note: Scoped IDs maintain their atomic canonical value
    let issue = RepoIssueId::for_scope(42);
    println!("  Scoped labeled:      {}", issue.labeled());
    // Output: User@scoped::42 (where 42 is the canonical ID)
}
