const std = @import("std");

pub const Device = struct {
    id: ?[]const u8 = null,
    is_active: bool,
    is_private_session: bool,
    is_restricted: bool,
    name: []const u8,
    type: []const u8,
    volume_percent: ?u7 = null,
    supports_volume: bool,
};

pub const RepeatState = enum {
    off,
    track,
    context,
};

pub const ExternalUrls = struct {
    spotify: []const u8,
};

pub const Context = struct {
    type: []const u8,
    href: []const u8,
    external_urls: ExternalUrls,
};

pub const CurrentlyPlayingType = enum {
    track,
    episode,
    ad,
    unknown,
};

pub const Image = struct {
    url: []const u8,
    height: ?u32 = null,
    width: ?u32 = null,
};

pub const Restrictions = struct {
    pub const Reason = enum {
        market,
        product,
        explicit,
    };

    reason: Reason,
};

pub const SimplifiedArtist = struct {
    external_urls: ExternalUrls,
    href: []const u8,
    id: []const u8,
    name: []const u8,
    type: enum { artist },
    uri: []const u8,
};

pub const Album = struct {
    pub const Type = enum {
        album,
        single,
        compilation,
    };

    album_type: Type,
    total_tracks: u32,
    available_markets: ?[]const []const u8 = null,
    external_urls: ExternalUrls,
    href: []const u8,
    id: []const u8,
    images: []const Image,
    name: []const u8,
    release_date: []const u8,
    release_date_precision: []const u8,
    restrictions: ?Restrictions = null,
    type: enum { album },
    uri: []const u8,
    artists: []const SimplifiedArtist,
};

pub const ExternalIds = struct {
    isc: ?[]const u8 = null,
    ean: ?[]const u8 = null,
    upc: ?[]const u8 = null,
};

pub const Track = struct {
    album: Album,
    artists: []const SimplifiedArtist,
    available_markets: ?[]const []const u8 = null,
    disc_number: u32,
    duration_ms: u32,
    explicit: bool,
    external_ids: ExternalIds,
    external_urls: ExternalUrls,
    href: []const u8,
    id: []const u8,
    is_playable: bool,
    restrictions: ?Restrictions = null,
    name: []const u8,
    popularity: u8,
    track_number: u32,
    type: enum { track },
    uri: []const u8,
    is_local: bool,
};

pub const Episode = struct {
    // description: []const u8,
    // html_description: []const u8,
    // duration_ms: u32,
    // explicit: bool,
    // external_urls: ExternalUrls,
    // href: []const u8,
    // id: []const u8,
    // images: []const Image,
    // is_externally_hosted: bool,
    // languages: []const []const u8,
    // name: []const u8,
    // release_date: []const u8,
    // release_date_precision: []const u8,
    // type: enum { episode },
    // uri: []const u8,
    // restrictions: ?Restrictions = null,
    // show: struct {},
};

pub const GetPlaybackStateResponse = struct {
    pub const Item = struct {
        value: union(enum) {
            track: Track,
            episode: Episode,
        },

        pub fn jsonStringify(self: *const Item, jws: anytype) !void {
            return switch (self.value) {
                inline else => |v| jws.write(v),
            };
        }

        pub fn jsonParse(
            allocator: std.mem.Allocator,
            source: anytype,
            options: std.json.ParseOptions,
        ) std.json.ParseError(@TypeOf(source.*))!Item {
            const value = try std.json.innerParse(std.json.Value, allocator, source, options);
            return jsonParseFromValue(allocator, value, options);
        }

        pub fn jsonParseFromValue(
            allocator: std.mem.Allocator,
            value: std.json.Value,
            options: std.json.ParseOptions,
        ) std.json.ParseFromValueError!Item {
            const Type = enum { track, episode };

            switch (value) {
                .object => |v| {
                    const item_type: Type = switch (v.get("type") orelse return error.UnexpectedToken) {
                        .string => |s| try std.json.parseFromValueLeaky(
                            Type,
                            allocator,
                            .{ .string = s },
                            options,
                        ),
                        else => return error.UnexpectedToken,
                    };

                    switch (item_type) {
                        .track => {
                            const res = try std.json.parseFromValueLeaky(
                                Track,
                                allocator,
                                .{ .object = v },
                                options,
                            );
                            return .{ .value = .{ .track = res } };
                        },
                        .episode => {
                            const res = try std.json.parseFromValueLeaky(
                                Episode,
                                allocator,
                                .{ .object = v },
                                options,
                            );
                            return .{ .value = .{ .episode = res } };
                        },
                    }
                },
                else => return error.UnexpectedToken,
            }
        }
    };

    device: Device,
    repeat_state: RepeatState,
    shuffle_state: bool,
    context: ?Context = null,
    timestamp: i128,
    progress_ms: ?u64 = null,
    is_playing: bool,
    item: ?Item = null,
    currently_playing_type: CurrentlyPlayingType,
    actions: struct {
        interrupting_playback: ?bool = null,
        pausing: ?bool = null,
        resuming: ?bool = null,
        seeking: ?bool = null,
        skipping_next: ?bool = null,
        skipping_prev: ?bool = null,
        toggling_repeat_context: ?bool = null,
        toggling_shuffle: ?bool = null,
        toggling_repeat_track: ?bool = null,
        transferring_playback: ?bool = null,
    },
    smart_shuffle: ?bool = null,
};
