import Foundation
import SwiftData
import UIKit

/// Syncs playlist metadata between iOS and macOS via a shared JSON file in the app's documents.
/// The JSON file can be shared through iCloud Drive, AirDrop, or any file transfer method.
///
/// Sync format: each device writes its own playlists to a JSON file.
/// The Mac app watches for this file and imports playlist data.
/// Matching is done by filename (not file path, since paths differ between devices).
@MainActor
final class SyncManager: ObservableObject {
    @Published var lastSyncDate: Date?
    @Published var isSyncing = false
    @Published var syncError: String?

    /// The shared sync directory — lives in the app's documents for easy access.
    /// Users can share this via iCloud Drive or Files app.
    static var syncDirectory: URL {
        let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let syncDir = docs.appendingPathComponent("Sync", isDirectory: true)
        try? FileManager.default.createDirectory(at: syncDir, withIntermediateDirectories: true)
        return syncDir
    }

    /// The sync file that contains exported playlist data.
    static var syncFileURL: URL {
        syncDirectory.appendingPathComponent("mixboard-playlists.json")
    }

    // MARK: - Export Playlists (iOS → JSON → Mac)

    /// Export all playlists to the sync JSON file.
    func exportPlaylists(_ playlists: [Playlist]) {
        isSyncing = true
        syncError = nil

        do {
            let payload = SyncPayload(
                version: 1,
                exportedAt: Date(),
                exportedFrom: deviceName,
                playlists: playlists.map { SyncPlaylist(from: $0) }
            )

            let encoder = JSONEncoder()
            encoder.dateEncodingStrategy = .iso8601
            encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
            let data = try encoder.encode(payload)
            try data.write(to: Self.syncFileURL, options: .atomic)

            lastSyncDate = Date()
            isSyncing = false
        } catch {
            syncError = error.localizedDescription
            isSyncing = false
        }
    }

    /// Import playlists from a sync JSON file (e.g. from Mac).
    func importPlaylists(from url: URL, context: ModelContext) throws -> [SyncPlaylist] {
        let data = try Data(contentsOf: url)
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        let payload = try decoder.decode(SyncPayload.self, from: data)
        return payload.playlists
    }

    /// Create actual Playlist models from synced data, matching tracks by filename.
    func mergeImportedPlaylists(_ syncPlaylists: [SyncPlaylist], existingTracks: [Track], context: ModelContext) -> (created: Int, matched: Int, unmatched: Int) {
        var totalCreated = 0
        var totalMatched = 0
        var totalUnmatched = 0

        for sp in syncPlaylists {
            let playlist = Playlist(name: sp.name, notes: sp.notes, color: sp.color)
            playlist.targetBPM = sp.targetBPM
            context.insert(playlist)
            totalCreated += 1

            for (position, se) in sp.entries.enumerated() {
                // Try to find matching track by filename
                let matchedTrack = existingTracks.first { $0.fileName == se.filename }

                if let track = matchedTrack {
                    let entry = PlaylistEntry(
                        position: position,
                        track: track,
                        crossfadeDuration: se.crossfadeDuration,
                        startOffset: se.startOffset,
                        endOffset: se.endOffset,
                        gainAdjustment: se.gainAdjustment,
                        notes: se.notes
                    )
                    playlist.entries.append(entry)
                    totalMatched += 1
                } else {
                    // Create a placeholder entry with no track (unmatched)
                    let entry = PlaylistEntry(
                        position: position,
                        track: nil,
                        crossfadeDuration: se.crossfadeDuration,
                        notes: "⚠ File not found: \(se.filename)\n\(se.notes)"
                    )
                    playlist.entries.append(entry)
                    totalUnmatched += 1
                }
            }
        }

        try? context.save()
        return (totalCreated, totalMatched, totalUnmatched)
    }

    // MARK: - Helpers

    private var deviceName: String {
        UIDevice.current.name
    }
}

// MARK: - Sync Data Models (Codable)

struct SyncPayload: Codable {
    let version: Int
    let exportedAt: Date
    let exportedFrom: String
    let playlists: [SyncPlaylist]
}

struct SyncPlaylist: Codable, Identifiable {
    let id: UUID
    let name: String
    let notes: String
    let color: String
    let dateCreated: Date
    let dateModified: Date
    let targetBPM: Double?
    let entries: [SyncEntry]

    init(from playlist: Playlist) {
        self.id = playlist.id
        self.name = playlist.name
        self.notes = playlist.notes
        self.color = playlist.color
        self.dateCreated = playlist.dateCreated
        self.dateModified = playlist.dateModified
        self.targetBPM = playlist.targetBPM
        self.entries = playlist.sortedEntries.map { SyncEntry(from: $0) }
    }
}

struct SyncEntry: Codable {
    let id: UUID
    let position: Int
    let filename: String           // matching key between devices
    let title: String
    let artist: String
    let album: String
    let duration: TimeInterval
    let bpm: Double?
    let musicalKey: String?
    let crossfadeDuration: TimeInterval
    let startOffset: TimeInterval
    let endOffset: TimeInterval
    let gainAdjustment: Double
    let notes: String

    init(from entry: PlaylistEntry) {
        self.id = entry.id
        self.position = entry.position
        self.filename = entry.track?.fileName ?? "unknown"
        self.title = entry.track?.title ?? "Unknown"
        self.artist = entry.track?.artist ?? ""
        self.album = entry.track?.album ?? ""
        self.duration = entry.track?.duration ?? 0
        self.bpm = entry.track?.bpm
        self.musicalKey = entry.track?.musicalKey
        self.crossfadeDuration = entry.crossfadeDuration
        self.startOffset = entry.startOffset
        self.endOffset = entry.endOffset
        self.gainAdjustment = entry.gainAdjustment
        self.notes = entry.notes
    }
}
