package api

import (
	"encoding/json"
	"fmt"
	"io/fs"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"arimelody-web/admin"
	"arimelody-web/global"
	"arimelody-web/controller"
	"arimelody-web/model"
)

func ServeAllArtists() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        var artists = []*model.Artist{}
        artists, err := controller.GetAllArtists(global.DB)
		if err != nil {
            fmt.Printf("FATAL: Failed to serve all artists: %s\n", err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}

		w.Header().Add("Content-Type", "application/json")
        encoder := json.NewEncoder(w)
        encoder.SetIndent("", "\t")
        err = encoder.Encode(artists)
		if err != nil {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
    })
}

func ServeArtist(artist *model.Artist) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		type (
			creditJSON struct {
                ID              string      `json:"id"`
                Title           string      `json:"title"`
                ReleaseDate     time.Time   `json:"releaseDate" db:"release_date"`
                Artwork         string      `json:"artwork"`
				Role            string      `json:"role"`
				Primary         bool        `json:"primary"`
			}
			artistJSON struct {
				*model.Artist
				Credits map[string]creditJSON `json:"credits"`
			}
		)

        show_hidden_releases := admin.GetSession(r) != nil

        var dbCredits []*model.Credit
        dbCredits, err := controller.GetArtistCredits(global.DB, artist.ID, show_hidden_releases)
		if err != nil {
            fmt.Printf("FATAL: Failed to retrieve artist credits for %s: %s\n", artist.ID, err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}

        var credits = map[string]creditJSON{}
        for _, credit := range dbCredits {
            credits[credit.Release.ID] = creditJSON{
                ID: credit.Release.ID,
                Title: credit.Release.Title,
                ReleaseDate: credit.Release.ReleaseDate,
                Artwork: credit.Release.Artwork,
                Role: credit.Role,
                Primary: credit.Primary,
            }
        }

		w.Header().Add("Content-Type", "application/json")
        encoder := json.NewEncoder(w)
        encoder.SetIndent("", "\t")
        err = encoder.Encode(artistJSON{
            Artist: artist,
            Credits: credits,
        })
		if err != nil {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
	})
}

func CreateArtist() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        var artist model.Artist
        err := json.NewDecoder(r.Body).Decode(&artist)
        if err != nil {
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
        }

        if artist.ID == "" {
            http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest)
            return
        }
        if artist.Name == "" { artist.Name = artist.ID }

        err = controller.CreateArtist(global.DB, &artist)
        if err != nil {
            if strings.Contains(err.Error(), "duplicate key") {
                http.Error(w, fmt.Sprintf("Artist %s already exists\n", artist.ID), http.StatusBadRequest)
                return
            }
            fmt.Printf("FATAL: Failed to create artist %s: %s\n", artist.ID, err)
            http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
            return
        }

        w.WriteHeader(http.StatusCreated)
    })
}

func UpdateArtist(artist *model.Artist) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        err := json.NewDecoder(r.Body).Decode(&artist)
        if err != nil {
            fmt.Printf("FATAL: Failed to update artist: %s\n", err)
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
        }

        if artist.Avatar == "" {
            artist.Avatar = "/img/default-avatar.png"
        } else {
            if strings.Contains(artist.Avatar, ";base64,") {
                var artworkDirectory = filepath.Join("uploads", "avatar")
                filename, err := HandleImageUpload(&artist.Avatar, artworkDirectory, artist.ID)

                // clean up files with this ID and different extensions
                err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error {
                    if path == filepath.Join(artworkDirectory, filename) { return nil }

                    withoutExt := strings.TrimSuffix(path, filepath.Ext(path))
                    if withoutExt != filepath.Join(artworkDirectory, artist.ID) { return nil }

                    return os.Remove(path)
                })
                if err != nil {
                    fmt.Printf("WARN: Error while cleaning up avatar files: %s\n", err)
                }

                artist.Avatar = fmt.Sprintf("/uploads/avatar/%s", filename)
            }
        }

        err = controller.UpdateArtist(global.DB, artist)
        if err != nil {
            if strings.Contains(err.Error(), "no rows") {
                http.NotFound(w, r)
                return
            }
            fmt.Printf("FATAL: Failed to update artist %s: %s\n", artist.ID, err)
            http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
        }
    })
}

func DeleteArtist(artist *model.Artist) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        err := controller.DeleteArtist(global.DB, artist.ID)
        if err != nil {
            if strings.Contains(err.Error(), "no rows") {
                http.NotFound(w, r)
                return
            }
            fmt.Printf("FATAL: Failed to delete artist %s: %s\n", artist.ID, err)
            http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
        }
    })
}
