# GraphQL Schema for MetaMuLate

This document defines the proposed GraphQL schema for integrating MetaMuLate with the Orchard Suite backend.

## Overview

The MetaMuLate module requires GraphQL queries/mutations for:
1. **Snowflake Queries** - Wrapped SQL queries executed via backend
2. **Golden Record Persistence** - Saving resolved metadata
3. **User Preferences** - Storing configuration and secrets securely

---

## Type Definitions

```graphql
# ============================================
# Core Types
# ============================================

"""
Data value with provenance tracking
"""
type DataValue {
  """The actual value"""
  val: String
  """URL to the source of this value"""
  url: String
}

input DataValueInput {
  val: String
  url: String
}

"""
Track metadata from discovery or enrichment
"""
type TrackMetadata {
  id: ID!
  
  # Core Identifiers
  isrc: DataValue
  iswc: DataValue
  upc: DataValue
  catalogNumber: DataValue
  barcode: DataValue
  
  # Basic Metadata
  trackName: DataValue!
  artist: DataValue!
  album: DataValue
  year: DataValue
  trackNumber: DataValue
  discNumber: DataValue
  duration: DataValue
  
  # Rights & Ownership
  pLine: DataValue
  label: DataValue
  country: DataValue
  
  # Credits
  composer: DataValue
  producer: DataValue
  writerArtists: DataValue
  featuredArtists: DataValue
  
  # Extended
  genre: DataValue
  styles: DataValue
  tags: DataValue
  contentRating: DataValue
  lyricsSnippet: DataValue
  
  # External IDs
  spotifyId: DataValue
  musicbrainzId: DataValue
  
  # Computed
  image: String
  authorityScore: Int
  completeness: Int
  source: String
}

"""
Golden Record - The reconciled truth version
"""
type GoldenRecord {
  id: ID!
  sourceTrackId: ID!
  
  # Audit fields
  createdAt: DateTime!
  modifiedAt: DateTime!
  createdBy: String
  approvedBy: String
  status: GoldenRecordStatus!
  
  # All metadata fields (same structure as TrackMetadata)
  isrc: DataValue
  iswc: DataValue
  upc: DataValue
  catalogNumber: DataValue
  trackName: DataValue!
  artist: DataValue!
  album: DataValue
  year: DataValue
  duration: DataValue
  pLine: DataValue
  label: DataValue
  composer: DataValue
  genre: DataValue
  
  # Audit trail
  sourceSelections: [SourceSelection!]
  
  # Confidence
  confidenceScore: Int
  image: String
}

enum GoldenRecordStatus {
  DRAFT
  PENDING
  APPROVED
  REJECTED
}

"""
Tracks which source was selected for each field
"""
type SourceSelection {
  fieldKey: String!
  sourceKey: String!
  selectedAt: DateTime!
  originalValue: DataValue!
  wasEdited: Boolean!
  editedValue: DataValue
}

input SourceSelectionInput {
  fieldKey: String!
  sourceKey: String!
  originalValue: DataValueInput!
  wasEdited: Boolean
  editedValue: DataValueInput
}

# ============================================
# Snowflake Query Types
# ============================================

"""
Snowflake track lookup result
"""
type SnowflakeTrackResult {
  trackName: String
  isrc: String
  label: String
  pLine: String
  releaseDate: String
  genreId: String
  lengthMinute: Int
  lengthSeconds: Int
}

"""
Snowflake query execution result
"""
type SnowflakeQueryResult {
  success: Boolean!
  data: [SnowflakeTrackResult!]
  error: String
  executionTimeMs: Int
  rowCount: Int
}

# ============================================
# User Preferences
# ============================================

"""
User's MetaMuLate configuration
"""
type MetamulateConfig {
  # API tokens (stored securely, not returned)
  hasAppleToken: Boolean!
  hasDiscogsToken: Boolean!
  hasGeniusToken: Boolean!
  
  # Snowflake config
  snowflakeCatalog: String
  snowflakeSchema: String
  snowflakeConnectionMethod: String
  
  # Matrix settings
  matrixColumns: [String!]!
  matrixMaxColumns: Int!
  
  # Timeout settings
  timeouts: MetamulateTimeouts!
  
  # Debug mode
  debugMode: Boolean!
}

type MetamulateTimeouts {
  apple: Int!
  discogs: Int!
  musicbrainz: Int!
  genius: Int!
  lyrics: Int!
  snowflake: Int!
  wikidata: Int!
}

input MetamulateTimeoutsInput {
  apple: Int
  discogs: Int
  musicbrainz: Int
  genius: Int
  lyrics: Int
  snowflake: Int
  wikidata: Int
}

input MetamulateConfigInput {
  appleToken: String
  discogsToken: String
  geniusToken: String
  snowflakeCatalog: String
  snowflakeSchema: String
  snowflakeConnectionMethod: String
  matrixColumns: [String!]
  matrixMaxColumns: Int
  timeouts: MetamulateTimeoutsInput
  debugMode: Boolean
}
```

---

## Queries

```graphql
type Query {
  """
  Execute a Snowflake track lookup
  Uses the authenticated user's Snowflake credentials via SSO
  """
  metamulateSnowflakeLookup(
    trackName: String!
    artistName: String!
    catalog: String
    schema: String
  ): SnowflakeQueryResult!

  """
  Get user's MetaMuLate configuration
  """
  metamulateConfig: MetamulateConfig!

  """
  Get a previously saved Golden Record
  """
  goldenRecord(id: ID!): GoldenRecord

  """
  List Golden Records for a user/session
  """
  goldenRecords(
    status: GoldenRecordStatus
    limit: Int = 50
    offset: Int = 0
  ): [GoldenRecord!]!

  """
  Search Golden Records by ISRC or track name
  """
  searchGoldenRecords(
    query: String!
    limit: Int = 20
  ): [GoldenRecord!]!
}
```

---

## Mutations

```graphql
type Mutation {
  """
  Save or update user's MetaMuLate configuration
  API tokens are stored securely and never returned
  """
  updateMetamulateConfig(
    input: MetamulateConfigInput!
  ): MetamulateConfig!

  """
  Create a new Golden Record from reconciled data
  """
  createGoldenRecord(
    sourceTrackId: ID!
    metadata: GoldenRecordInput!
  ): GoldenRecord!

  """
  Update an existing Golden Record
  """
  updateGoldenRecord(
    id: ID!
    metadata: GoldenRecordInput!
  ): GoldenRecord!

  """
  Change Golden Record status (approve/reject)
  """
  updateGoldenRecordStatus(
    id: ID!
    status: GoldenRecordStatus!
    approverNotes: String
  ): GoldenRecord!

  """
  Delete a Golden Record
  """
  deleteGoldenRecord(id: ID!): Boolean!

  """
  Bulk create Golden Records from session export
  """
  bulkCreateGoldenRecords(
    records: [GoldenRecordInput!]!
  ): BulkCreateResult!
}

input GoldenRecordInput {
  isrc: DataValueInput
  iswc: DataValueInput
  upc: DataValueInput
  catalogNumber: DataValueInput
  trackName: DataValueInput!
  artist: DataValueInput!
  album: DataValueInput
  year: DataValueInput
  duration: DataValueInput
  pLine: DataValueInput
  label: DataValueInput
  composer: DataValueInput
  genre: DataValueInput
  image: String
  sourceSelections: [SourceSelectionInput!]
  status: GoldenRecordStatus
}

type BulkCreateResult {
  success: Boolean!
  created: Int!
  failed: Int!
  errors: [String!]
}
```

---

## Backend Implementation Notes

### Snowflake Integration

The `metamulateSnowflakeLookup` query should:
1. Use the user's SSO credentials (via existing Orchard SSO integration)
2. Execute the parameterized SQL query from `trackLookup.sql`
3. Apply rate limiting (2 queries/second recommended)
4. Return results in typed structure

**Example Resolver:**
```python
@strawberry.field
async def metamulate_snowflake_lookup(
    self,
    info: Info,
    track_name: str,
    artist_name: str,
    catalog: str | None = None,
    schema: str | None = None,
) -> SnowflakeQueryResult:
    user = get_current_user(info)
    snowflake_conn = get_snowflake_connection(user)
    
    # Escape inputs for ILIKE
    safe_track = track_name.replace("'", "''")
    safe_artist = artist_name.replace("'", "''")
    
    query = TRACK_LOOKUP_SQL.format(
        catalog=catalog or DEFAULT_CATALOG,
        schema=schema or DEFAULT_SCHEMA,
        trackName=safe_track,
        artistName=safe_artist,
    )
    
    result = await snowflake_conn.execute(query)
    return SnowflakeQueryResult(
        success=True,
        data=result.rows,
        execution_time_ms=result.elapsed_ms,
        row_count=len(result.rows),
    )
```

### Secure Token Storage

API tokens (Apple, Discogs, Genius) should be:
1. Stored in encrypted user preferences table
2. Never returned in GraphQL responses
3. Only used server-side for verification endpoints

### Golden Record Persistence

Golden Records should be stored with:
1. Full audit trail (who, when, what changed)
2. Soft delete support
3. Export capability to JSON/CSV

---

## Client Usage Examples

### Snowflake Lookup
```typescript
const { data } = useQuery(SNOWFLAKE_LOOKUP, {
  variables: {
    trackName: "Get Lucky",
    artistName: "Daft Punk",
  },
});
```

### Save Golden Record
```typescript
const [saveRecord] = useMutation(CREATE_GOLDEN_RECORD);

await saveRecord({
  variables: {
    sourceTrackId: track.id,
    metadata: {
      isrc: { val: "USRC11234567", url: "https://apple.com/..." },
      trackName: { val: "Get Lucky", url: "https://apple.com/..." },
      artist: { val: "Daft Punk", url: "https://apple.com/..." },
      // ... other fields
      sourceSelections: [
        { fieldKey: "isrc", sourceKey: "apple", ... },
      ],
    },
  },
});
```
