//
//  Level.swift
//  DashSmash
//
//  Procedural level data generated from a song/band seed.
//
//  Obstacles live on a beat timeline (Double, in beats from the song start),
//  not raw points. The scene maps beat→world-x using the level's pointsPerBeat
//  and the player's on-screen x offset, so the moment the player TOUCHES an
//  obstacle is the moment its corresponding music beat plays.
//

import Foundation
import CoreGraphics

enum GameMode: String, Codable {
    case cube       // normal: tap to jump
    case ship       // hold to fly up, release to fall
    case gravity    // gravity inverted (tap jumps "down")
    case mini       // smaller cube, faster jumps
}

enum ObstacleKind: String, Codable {
    case spike
    case block
    case ceilingSpike
    case floatingSpike
    case ceilingBlock
    case beatOrb
    /// Collectible. Gives points on contact and disappears. Never harms.
    case coin
    /// Helpful pad sitting on the floor. On contact, launches the player
    /// upward harder than a normal jump.
    case jumpPad
}

enum LevelStyle: String, Codable, CaseIterable {
    case chill          // wide spacing, mostly cube, room to breathe
    case dancey         // single hits on every other beat, locked groove
    case aggressive     // dense, lots of multi-spike combos
    case progressive    // grows in intensity from sparse to dense
    case spacey         // mostly ship / gravity, gliding feel
    case stuttery       // short sections, frequent mode flips
    case anthem         // verse / chorus contrast, big drops
}

struct Obstacle: Codable {
    let kind: ObstacleKind
    /// When the obstacle aligns with the music (beats from level start).
    let beat: Double
    /// Vertical offset above the floor (for blocks).
    let yOffset: CGFloat
    /// Visual width and height in points.
    let width: CGFloat
    let height: CGFloat
}

struct LevelSection: Codable {
    let mode: GameMode
    /// When this section starts (beats from level start).
    let startBeat: Double
    let obstacles: [Obstacle]
    /// Label shown briefly when entering the section.
    let label: String
}

struct Level: Codable {
    let id: UUID
    let songName: String
    let bandName: String
    let bpm: Double
    /// World scaling: how many points correspond to one beat of music.
    let pointsPerBeat: CGFloat
    /// Total duration of the level, in beats.
    let totalBeats: Double
    let sections: [LevelSection]
    let primaryHue: CGFloat
    let secondaryHue: CGFloat
    let style: LevelStyle
    /// If set, the level is backed by a real audio track instead of the
    /// procedural synth. Prefixed: "am:<MusicKit Song ID>" or
    /// "lib:<MPMediaItem persistentID>".
    let trackSourceID: String?

    // Derived

    /// Points the world advances per second of real time.
    var scrollSpeed: CGFloat {
        pointsPerBeat * CGFloat(bpm / 60.0)
    }

    /// Total horizontal length of the level in points (excluding the
    /// player-x offset; the scene adds that at spawn time).
    var totalWidth: CGFloat {
        CGFloat(totalBeats) * pointsPerBeat
    }

    /// Total play duration in seconds.
    var durationSeconds: Double {
        totalBeats * 60.0 / bpm
    }

    var displayTitle: String {
        if songName.isEmpty && bandName.isEmpty { return "Untitled" }
        if bandName.isEmpty { return songName }
        if songName.isEmpty { return bandName }
        return "\(songName) — \(bandName)"
    }
}
