package main

import (
	"bufio"
	"encoding/json"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

type spotifyUser struct {
	UserID      string `json:"user_id"`
	CountryCode string `json:"country"`
}

type spotifyTrack struct {
	TrackID      string `json:"track_id"`
	TrackURI     string `json:"uri"`
	ISRC         string `json:"isrc"`
	AlbumUPC     string `json:"album_code"`
	AlbumArtist  string `json:"album_artist"`
	AlbumName    string `json:"album_name"`
	TrackArtists string `json:"track_artists"`
	TrackName    string `json:"track_name"`
}

var s3SourceBucket = "sme-stream-reports"
var s3TargetBucket = "sme-aggregated-stream-reports"
var s3Service = s3.New(session.New(), &aws.Config{Region: aws.String("eu-west-1"), S3UseAccelerate: aws.Bool(true)})

var cacheTracksKey string
var cacheTracks map[string]spotifyTrack
var cacheUsersKey string
var cacheUsers map[string]string

func makeSpotifySourceUsersS3Key(date string, licensor string) string {
	if date >= "2017-09-04" && licensor == "sony" {
		return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v2.0_users.ndjson"
	}
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1.0_users.ndjson"
}

func makeSpotifySourceTracksS3Key(date string, licensor string) string {
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1.0_tracks.ndjson"
}

func makeSpotifySourceStreamsS3Key(date string, licensor string, countryCode string) string {
	if date >= "2017-09-04" && licensor == "sony" {
		return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v2.2_" + countryCode + "_streams.ndjson"
	}
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1.0_" + countryCode + "_streams.ndjson"
}

func makeSpotifyTargetStreamsTracksS3Key(date string, licensor string, countryCode string) string {
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1_" + countryCode + "_streams_tracks.ndjson"
}

func makeSpotifyTargetStreamsPlaylistsS3Key(date string, licensor string, countryCode string) string {
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1_" + countryCode + "_streams_playlists.ndjson"
}

func makeSpotifyTargetStreamsPlaylistTracksS3Key(date string, licensor string, countryCode string) string {
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1_" + countryCode + "_streams_playlist_tracks.ndjson"
}

func makeSpotifyTargetStreamsSummaryS3Key(date string, licensor string, countryCode string) string {
	return "spotify/" + date + "/spotify_" + date + "_" + licensor + "_v1_" + countryCode + "_streams_summary.ndjson"
}

func targetSpotifyStreamsSummaryFileExists(date string, licensor string, countryCode string) bool {
	input := &s3.HeadObjectInput{
		Bucket: aws.String(s3TargetBucket),
		Key:    aws.String(makeSpotifyTargetStreamsSummaryS3Key(date, licensor, countryCode)),
	}

	result, err := s3Service.HeadObject(input)
	if err != nil {
		return false
	}
	_ = result

	return true
}

func sourceSpotifyTracksFileExists(date string, licensor string) bool {
	input := &s3.HeadObjectInput{
		Bucket: aws.String(s3SourceBucket),
		Key:    aws.String(makeSpotifySourceTracksS3Key(date, licensor)),
	}

	result, err := s3Service.HeadObject(input)
	if err != nil {
		return false
	}
	_ = result

	return true
}

func sourceSpotifyUsersFileExists(date string, licensor string) bool {
	input := &s3.HeadObjectInput{
		Bucket: aws.String(s3SourceBucket),
		Key:    aws.String(makeSpotifySourceUsersS3Key(date, licensor)),
	}

	result, err := s3Service.HeadObject(input)
	if err != nil {
		return false
	}
	_ = result

	return true
}

func sourceSpotifyStreamsFileExists(date string, licensor string, countryCode string) bool {
	input := &s3.HeadObjectInput{
		Bucket: aws.String(s3SourceBucket),
		Key:    aws.String(makeSpotifySourceStreamsS3Key(date, licensor, countryCode)),
	}

	result, err := s3Service.HeadObject(input)
	if err != nil {
		return false
	}
	_ = result

	return true
}

func getSpotifyUsers(date string, licensor string) map[string]string {
	cacheKey := date + licensor

	if cacheUsersKey != cacheKey {
		cacheUsers = make(map[string]string)
	} else {
		return cacheUsers
	}

	input := &s3.GetObjectInput{
		Bucket: aws.String(s3SourceBucket),
		Key:    aws.String(makeSpotifySourceUsersS3Key(date, licensor)),
	}

	result, err := s3Service.GetObject(input)
	if err != nil {
		log.Fatal(err)
	}

	users := make(map[string]string)

	scanner := bufio.NewScanner(result.Body)
	for scanner.Scan() {
		var user spotifyUser
		json.Unmarshal([]byte(scanner.Text()), &user)
		user.CountryCode = strings.ToLower(user.CountryCode)
		users[user.UserID] = user.CountryCode
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	cacheUsersKey = cacheKey
	cacheUsers = users

	return users
}

func getSpotifyTracks(date string, licensor string) map[string]spotifyTrack {
	cacheKey := date + licensor

	if cacheTracksKey != cacheKey {
		cacheTracks = make(map[string]spotifyTrack)
	} else {
		return cacheTracks
	}

	input := &s3.GetObjectInput{
		Bucket: aws.String(s3SourceBucket),
		Key:    aws.String(makeSpotifySourceTracksS3Key(date, licensor)),
	}

	result, err := s3Service.GetObject(input)
	if err != nil {
		log.Fatal(err)
	}

	tracks := make(map[string]spotifyTrack)

	scanner := bufio.NewScanner(result.Body)
	for scanner.Scan() {
		var track spotifyTrack
		json.Unmarshal([]byte(scanner.Text()), &track)
		tracks[track.TrackID] = track
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	cacheTracksKey = cacheKey
	cacheTracks = tracks

	return tracks
}
