// Row shapes for the muzick schema, extracted verbatim from db.service.ts so
// that file is about behaviour and this one about data. Re-exported from
// db.service.ts, so an existing `import { Track } from '../services/db.service.js'`
// keeps working.

export interface Artist {
  id: string;
  name: string;
  mbid?: string | null;
  discogs_id?: string | null;
  image_path?: string | null;
}

export interface Album {
  id: string;
  artist_id: string;
  title: string;
  year?: number | null;
  /** Canonical MusicBrainz release-group first-release-date (YYYY-MM-DD). */
  release_date?: string | null;
  artwork_id?: string | null;
}

export interface TrackArtist {
  id: string;
  name: string;
  role: 'main' | 'featured';
}

export interface Track {
  id: string;
  path: string;
  hash: string;
  title: string;
  artist: string;
  album_id: string;
  duration: number;
  state: string;
  play_count: number;
  skip_count: number;
  dislike_count: number;
  last_played_at?: Date | null;
  /** Denormalised from albums.release_date by a database trigger. */
  release_date?: string | null;
  mtime?: number | null;
  source_type: string;
  artists?: TrackArtist[];
}

export const FEEDBACK_ACTIONS = ['promoted', 'disliked', 'skipped', 'deleted_permanent'] as const;
export type FeedbackAction = (typeof FEEDBACK_ACTIONS)[number];

export interface HistoryEntry extends Track {
  history_id: string;
  batch_id: string | null;
  played_at: Date;
  completed: boolean;
}

export interface Genre {
  id: string;
  name: string;
  parent_id?: string | null;
  track_count?: number;
}

export interface DislikeEntry {
  track_id: string;
  disliked_at: Date;
  warned_at: Date | null;
  deleted_at: Date | null;
  grace_hours: number;
  state: string; // 'HIDDEN' | 'WARNED' | 'DELETED'
  track_title: string;
  track_artist: string;
  track_path: string;
}

export interface ArtistWithAlbums {
  id: string;
  name: string;
  mbid?: string | null;
  discogs_id?: string | null;
  image_path?: string | null;
  albums: Album[];
}

export interface AlbumWithTracks {
  id: string;
  artist_id: string;
  title: string;
  year?: number | null;
  artwork_id?: string | null;
  tracks: Track[];
}


// ---------------------------------------------------------------------------
// v2 Recommendation Engine types
// ---------------------------------------------------------------------------

export interface Claim {
  id: string;
  user_id: string | null;
  subject_type: string;
  subject_id: string;
  predicate: string;
  object_type: string;
  object_id: string;
  source: string;
  confidence: number;
  evidence_at: Date;
  last_reinforced_at: Date;
  raw: unknown | null;
  created_at: Date;
}

export interface Evidence {
  id: string;
  user_id: string;
  entity_type: string;
  entity_id: string;
  signal: string;
  profile: string;
  weight: number;
  context: unknown | null;
  created_at: Date;
}

export interface ListenerBelief {
  user_id: string;
  profile: string;
  entity_type: string;
  entity_id: string;
  dimension: string;
  value: number;
  confidence: number;
  evidence_count: number;
  last_reinforced_at: Date;
  last_decayed_at: Date;
}

/**
 * Stable pseudo-entity IDs for audio preference buckets. listener_beliefs uses
 * UUID entity IDs for every entity type, while audio dimensions are values rather
 * than rows in their own table. Keeping the IDs fixed makes these beliefs usable
 * by the planner without introducing a second, unbounded vocabulary.
 */
export const AUDIO_PREFERENCE_BUCKETS = {
  energy: {
    low: '10000000-0000-0000-0000-000000000001',
    medium: '10000000-0000-0000-0000-000000000002',
    high: '10000000-0000-0000-0000-000000000003',
  },
  bpm: {
    slow: '10000000-0000-0000-0000-000000000011',
    medium: '10000000-0000-0000-0000-000000000012',
    fast: '10000000-0000-0000-0000-000000000013',
  },
  valence: {
    low: '10000000-0000-0000-0000-000000000021',
    neutral: '10000000-0000-0000-0000-000000000022',
    high: '10000000-0000-0000-0000-000000000023',
  },
} as const;

export interface ClaimEdge {
  subjectType: string;
  subjectId: string;
  predicate: string;
  objectType: string;
  objectId: string;
  fusedValue: number;
}

export interface SessionState {
  session_id: string;
  user_id: string;
  started_at: Date;
  last_interaction: Date;
  context: string | null;
  state_vector: Record<string, unknown>;
}

export interface DiversityBudget {
  user_id: string;
  dimension: string;
  budget_share: number;
  horizon_min: number;
}

export interface RepetitionRule {
  user_id: string;
  dimension: string;
  min_distance: number;
}

// ---------------------------------------------------------------------------
// Vibe v2 durable session ledger
// ---------------------------------------------------------------------------

export const VIBE_SESSION_STATUSES = ['active', 'paused', 'ended', 'expired', 'replaced'] as const;
export type VibeSessionStatus = (typeof VIBE_SESSION_STATUSES)[number];

export interface VibeSession {
  id: string;
  user_id: string;
  status: VibeSessionStatus;
  seed_track_id: string | null;
  context: Record<string, unknown>;
  policy_version: string;
  started_at: Date;
  last_event_at: Date;
  ended_at: Date | null;
}

export interface VibeEvent {
  id: string;
  client_event_id: string | null;
  session_id: string;
  user_id: string;
  track_id: string | null;
  type: string;
  occurred_at: Date;
  position_ms: number | null;
  duration_ms: number | null;
  payload: Record<string, unknown>;
}

export interface RecordedVibeEvent {
  event: VibeEvent;
  /** False when a retried client_event_id returned the original event. */
  inserted: boolean;
}

export interface VibePlanVersion {
  id: string;
  session_id: string;
  version: number;
  reason: string;
  state_snapshot: Record<string, unknown>;
  objective_snapshot: Record<string, unknown>;
  created_at: Date;
}

export interface VibePlanItem {
  plan_version_id: string;
  ordinal: number;
  track_id: string;
  slot_role: string | null;
  candidate_source: string;
  score: number;
  score_breakdown: Record<string, unknown>;
  explanation: unknown;
  committed: boolean;
}

export interface VibePlan extends VibePlanVersion {
  items: VibePlanItem[];
}

/** Derived session-director memory. The ledger remains authoritative; this
 * compact row makes fingerprints, bounded goals, and exploration state cheap
 * to read while planning. */
export interface VibeSessionProfile {
  session_id: string;
  user_id: string;
  fingerprint: Record<string, unknown>;
  goals: Record<string, unknown>;
  exploration_coefficient: number;
  discovery_radius: number;
  updated_at: Date;
}
