use std::collections::HashMap;

use postgres::GenericConnection as PostgresConnection;

use db::pg::RowsExtension;
use library::Result;

const MAX_TOP_ALBUMS: i64 = 10;

#[derive(RustcEncodable)]
struct Track {
    name: String,
    is_favorite: bool,
    scrobbles: Vec<i32>,
}

#[derive(RustcEncodable)]
pub struct Album {
    name: String,
    tracks: HashMap<i32, Track>,
}

#[derive(RustcEncodable)]
pub struct TopAlbum {
    label: String,
    value: i32,
}

pub type TopAlbums = Vec<TopAlbum>;

pub fn load_album(conn: &PostgresConnection, album_id: i32) -> Result<Option<Album>> {
    let rows = try!(conn.query("SELECT name FROM albums WHERE id = $1", &[&album_id]));
    if rows.len() == 0 {
        return Ok(None);
    }
    let name: String = rows.get(0).get("name");

    let mut tracks = HashMap::<i32, Track>::new();
    let query = "SELECT id, name, is_favorite FROM tracks WHERE album_id = $1";
    for row in &try!(conn.query(query, &[&album_id])) {
        tracks.insert(row.get("id"),
                      Track {
                          name: row.get("name"),
                          is_favorite: row.get("is_favorite"),
                          scrobbles: Vec::new(),
                      });
    }

    let mut ids = tracks.keys().fold(String::new(), |acc, id| format!("{}{},", acc, id));
    ids.pop();
    let query = format!("SELECT track_id, timestamp FROM scrobbles WHERE track_id IN({})",
                        ids);
    let scrobbles = try!(conn.query(&query, &[]));
    for row in &scrobbles {
        let track_id: i32 = row.get("track_id");
        let timestamp: i32 = row.get("timestamp");
        if let Some(track) = tracks.get_mut(&track_id) {
            track.scrobbles.push(timestamp)
        }
    }

    Ok(Some(Album {
        name: name,
        tracks: tracks,
    }))
}

pub fn load_top_albums(conn: &PostgresConnection) -> Result<TopAlbums> {
    let query = r#"
        SELECT
            artists.name as artist,
            albums.name as album,
            albums.plays as plays
        FROM albums
        LEFT JOIN artists ON artists.id = albums.artist_id
        ORDER BY albums.plays DESC
        OFFSET 0 LIMIT $1
    "#;
    let rows = try!(conn.query(query, &[&MAX_TOP_ALBUMS]));
    let mut albums = TopAlbums::with_capacity(MAX_TOP_ALBUMS as usize);
    for row in &rows {
        let artist: String = row.get("artist");
        let album: String = row.get("album");
        albums.push(TopAlbum {
            label: format!("{} - {}", artist, album),
            value: row.get("plays"),
        });
    }
    Ok(albums)
}

pub fn total_albums(conn: &PostgresConnection) -> Result<i64> {
    Ok(try!(conn.query("SELECT COUNT(*) FROM albums", &[])).fetch_column())
}