import { BUrn } from "./burn";
import { taskEither as TE } from "fp-ts";

export type Credentials = { username: string; password: string };

export type AuthSuccess = {
  serviceToken: string;
  userId: string;
  nickname: string;
};

export class AuthFailure extends Error {
  constructor(message: string) {
    super(message);
  }
};

export type ArtistSummary = {
  id: string | undefined;
  name: string;
  image: BUrn | undefined;
};

export type SimilarArtist = ArtistSummary & { inLibrary: boolean };

export type Artist = ArtistSummary & {
  albums: AlbumSummary[];
  similarArtists: SimilarArtist[]
};

export type AlbumSummary = {
  id: string;
  name: string;
  year: string | undefined;
  genre: Genre | undefined;
  coverArt: BUrn | undefined;

  artistName: string | undefined;
  artistId: string | undefined;
};

export type Album = AlbumSummary & {};

export type Genre = {
  name: string;
  id: string;
}

export type Rating = {
  love: boolean;
  stars: number;
}

export type Track = {
  id: string;
  name: string;
  mimeType: string;
  duration: number;
  number: number | undefined;
  genre: Genre | undefined;
  coverArt: BUrn | undefined;
  album: AlbumSummary;
  artist: ArtistSummary;
  rating: Rating;
};

export type Paging = {
  _index: number | undefined;
  _count: number | undefined;
};

export type Result<T> = {
  results: T[];
  total: number;
};

export function slice2<T>({ _index, _count }: Partial<Paging> = {}) {
  const i = _index || 0;
  return (things: T[]): [T[], number] => [
    _count ? things.slice(i, i + _count) : things.slice(i),
    things.length,
  ];
}

export const asResult = <T>([results, total]: [T[], number]) => ({
  results,
  total,
});

export type ArtistQuery = Paging;

export type AlbumQueryType = 'alphabeticalByArtist' | 'alphabeticalByName' | 'byGenre' | 'random' | 'recentlyPlayed' | 'mostPlayed' | 'recentlyAdded' | 'favourited' | 'starred';

export type AlbumQuery = Paging & {
  type: AlbumQueryType;
  genre?: string;
};

export const artistToArtistSummary = (it: Artist): ArtistSummary => ({
  id: it.id,
  name: it.name,
  image: it.image
});

export const albumToAlbumSummary = (it: Album): AlbumSummary => ({
  id: it.id,
  name: it.name,
  year: it.year,
  genre: it.genre,
  artistName: it.artistName,
  artistId: it.artistId,
  coverArt: it.coverArt
});

export const playlistToPlaylistSummary = (it: Playlist): PlaylistSummary => ({
  id: it.id,
  name: it.name
})

export type StreamingHeader = "content-type" | "content-length" | "content-range" | "accept-ranges";

export type TrackStream = {
  status: number;
  headers: Record<StreamingHeader, string>;
  stream: any;
};

export type CoverArt = {
  contentType: string;
  data: Buffer;
}

export type PlaylistSummary = {
  id: string,
  name: string
}

export type Playlist = PlaylistSummary & {
  entries: Track[]
}

export type Sortable = {
  sortName: string
}

export const range = (size: number) => [...Array(size).keys()];

export const asArtistAlbumPairs = (artists: Artist[]): [Artist, Album][] =>
  artists.flatMap((artist) =>
    artist.albums.map((album) => [artist, album] as [Artist, Album])
  );

export interface MusicService {
  generateToken(credentials: Credentials): TE.TaskEither<AuthFailure, AuthSuccess>;
  refreshToken(serviceToken: string): TE.TaskEither<AuthFailure, AuthSuccess>;
  login(serviceToken: string): Promise<MusicLibrary>;
}

export interface MusicLibrary {
  artists(q: ArtistQuery): Promise<Result<ArtistSummary & Sortable>>;
  artist(id: string): Promise<Artist>;
  albums(q: AlbumQuery): Promise<Result<AlbumSummary>>;
  album(id: string): Promise<Album>;
  tracks(albumId: string): Promise<Track[]>;
  track(trackId: string): Promise<Track>;
  genres(): Promise<Genre[]>;
  stream({
    trackId,
    range,
  }: {
    trackId: string;
    range: string | undefined;
  }): Promise<TrackStream>;
  rate(trackId: string, rating: Rating): Promise<boolean>;
  coverArt(coverArtURN: BUrn, size?: number): Promise<CoverArt | undefined>;
  nowPlaying(id: string): Promise<boolean>
  scrobble(id: string): Promise<boolean>
  searchArtists(query: string): Promise<ArtistSummary[]>;
  searchAlbums(query: string): Promise<AlbumSummary[]>;
  searchTracks(query: string): Promise<Track[]>;
  playlists(): Promise<PlaylistSummary[]>;
  playlist(id: string): Promise<Playlist>;
  createPlaylist(name: string): Promise<PlaylistSummary>
  deletePlaylist(id: string): Promise<boolean>
  addToPlaylist(playlistId: string, trackId: string): Promise<boolean>
  removeFromPlaylist(playlistId: string, indicies: number[]): Promise<boolean>
  similarSongs(id: string): Promise<Track[]>;
  topSongs(artistId: string): Promise<Track[]>;
}
