package google_photo

import (
	"fmt"
	"net/http"

	"github.com/AlliotTech/openalist/drivers/base"
	"github.com/go-resty/resty/v2"
)

// do others that not defined in Driver interface

const (
	FETCH_ALL = "all"
	FETCH_ALBUMS = "albums"
	FETCH_ROOT = "root"
	FETCH_SHARE_ALBUMS = "share_albums"
)

func (d *GooglePhoto) refreshToken() error {
	url := "https://www.googleapis.com/oauth2/v4/token"
	var resp base.TokenResp
	var e TokenError
	_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
		SetFormData(map[string]string{
			"client_id":     d.ClientID,
			"client_secret": d.ClientSecret,
			"refresh_token": d.RefreshToken,
			"grant_type":    "refresh_token",
		}).Post(url)
	if err != nil {
		return err
	}
	if e.Error != "" {
		return fmt.Errorf(e.Error)
	}
	d.AccessToken = resp.AccessToken
	return nil
}

func (d *GooglePhoto) request(url string, method string, callback base.ReqCallback, resp interface{}, headers map[string]string) ([]byte, error) {
	req := base.RestyClient.R()
	req.SetHeader("Authorization", "Bearer "+d.AccessToken)
	req.SetHeader("Accept-Encoding", "gzip")
	if headers != nil {
		req.SetHeaders(headers)
	}

	if callback != nil {
		callback(req)
	}
	if resp != nil {
		req.SetResult(resp)
	}
	var e Error
	req.SetError(&e)
	res, err := req.Execute(method, url)
	if err != nil {
		return nil, err
	}
	if e.Error.Code != 0 {
		if e.Error.Code == 401 {
			err = d.refreshToken()
			if err != nil {
				return nil, err
			}
			return d.request(url, method, callback, resp, headers)
		}
		return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
	}
	return res.Body(), nil
}

func (d *GooglePhoto) getFiles(id string) ([]MediaItem, error) {
	switch id {
	case FETCH_ALL:
		return d.getAllMedias()
	case FETCH_ALBUMS:
		return d.getAlbums()
	case FETCH_SHARE_ALBUMS:
		return d.getShareAlbums()
	case FETCH_ROOT:
		return d.getFakeRoot()
	default:
		return d.getMedias(id)
	}
}

func (d *GooglePhoto) getFakeRoot() ([]MediaItem, error) {
	return []MediaItem{
		{
			Id: FETCH_ALL,
			Title: "全部媒体",
		},
		{
			Id: FETCH_ALBUMS,
			Title: "全部影集",
		},
		{
			Id: FETCH_SHARE_ALBUMS,
			Title: "共享影集",
		},
	}, nil
}

func (d *GooglePhoto) getAlbums() ([]MediaItem, error) {
	return d.fetchItems(
		"https://photoslibrary.googleapis.com/v1/albums",
		map[string]string{
			"fields":    "albums(id,title,coverPhotoBaseUrl),nextPageToken",
			"pageSize":  "50",
			"pageToken": "first",
		},
		http.MethodGet)
}

func (d *GooglePhoto) getShareAlbums() ([]MediaItem, error) {
	return d.fetchItems(
		"https://photoslibrary.googleapis.com/v1/sharedAlbums",
		map[string]string{
			"fields":    "sharedAlbums(id,title,coverPhotoBaseUrl),nextPageToken",
			"pageSize":  "50",
			"pageToken": "first",
		},
		http.MethodGet)
}

func (d *GooglePhoto) getMedias(albumId string) ([]MediaItem, error) {
	return d.fetchItems(
		"https://photoslibrary.googleapis.com/v1/mediaItems:search",
		map[string]string{
			"fields":    "mediaItems(id,baseUrl,mimeType,mediaMetadata,filename),nextPageToken",
			"pageSize":  "100",
			"albumId": albumId,
			"pageToken": "first",
		}, http.MethodPost)
}

func (d *GooglePhoto) getAllMedias() ([]MediaItem, error) {
	return d.fetchItems(
		"https://photoslibrary.googleapis.com/v1/mediaItems",
		map[string]string{
			"fields":    "mediaItems(id,baseUrl,mimeType,mediaMetadata,filename),nextPageToken",
			"pageSize":  "100",
			"pageToken": "first",
		},
		http.MethodGet)
}

func (d *GooglePhoto) getMedia(id string) (MediaItem, error) {
	var resp MediaItem

	query := map[string]string{
		"fields": "mediaMetadata,baseUrl,mimeType",
	}
	_, err := d.request(fmt.Sprintf("https://photoslibrary.googleapis.com/v1/mediaItems/%s", id), http.MethodGet, func(req *resty.Request) {
		req.SetQueryParams(query)
	}, &resp, nil)
	if err != nil {
		return resp, err
	}

	return resp, nil
}

func (d *GooglePhoto) fetchItems(url string, query map[string]string, method string) ([]MediaItem, error){
	res := make([]MediaItem, 0)
	for query["pageToken"] != "" {
		if query["pageToken"] == "first" {
			query["pageToken"] = ""
		}
		var resp Items

		_, err := d.request(url, method, func(req *resty.Request) {
			req.SetQueryParams(query)
		}, &resp, nil)
		if err != nil {
			return nil, err
		}
		query["pageToken"] = resp.NextPageToken
		res = append(res, resp.MediaItems...)
		res = append(res, resp.Albums...)
		res = append(res, resp.SharedAlbums...)
	}
	return res, nil
}
