package twitch

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/pkg/errors"
)

func (c *Client) AddChannelVIP(ctx context.Context, broadcasterName, userName string) error {
	broadcaster, err := c.GetIDForUsername(broadcasterName)
	if err != nil {
		return errors.Wrap(err, "getting ID for broadcaster name")
	}

	userID, err := c.GetIDForUsername(userName)
	if err != nil {
		return errors.Wrap(err, "getting ID for user name")
	}

	return errors.Wrap(
		c.Request(ClientRequestOpts{
			AuthType: AuthTypeBearerToken,
			Context:  ctx,
			Method:   http.MethodPost,
			OKStatus: http.StatusNoContent,
			URL:      fmt.Sprintf("https://api.twitch.tv/helix/channels/vips?broadcaster_id=%s&user_id=%s", broadcaster, userID),
		}),
		"executing request",
	)
}

func (c *Client) ModifyChannelInformation(ctx context.Context, broadcasterName string, game, title *string) error {
	if game == nil && title == nil {
		return errors.New("netiher game nor title provided")
	}

	broadcaster, err := c.GetIDForUsername(broadcasterName)
	if err != nil {
		return errors.Wrap(err, "getting ID for broadcaster name")
	}

	data := struct {
		GameID *string `json:"game_id,omitempty"`
		Title  *string `json:"title,omitempty"`
	}{
		Title: title,
	}

	switch {
	case game == nil:
		// We don't set the GameID

	case (*game)[0] == '@':
		// We got an ID and don't need to resolve
		gameID := (*game)[1:]
		data.GameID = &gameID

	default:
		categories, err := c.SearchCategories(ctx, *game)
		if err != nil {
			return errors.Wrap(err, "searching for game")
		}

		switch len(categories) {
		case 0:
			return errors.New("no matching game found")

		case 1:
			data.GameID = &categories[0].ID

		default:
			// Multiple matches: Search for exact one
			for _, c := range categories {
				if strings.EqualFold(c.Name, *game) {
					gid := c.ID
					data.GameID = &gid
					break
				}
			}

			if data.GameID == nil {
				// No exact match found: This is an error
				return errors.New("no exact game match found")
			}
		}
	}

	body := new(bytes.Buffer)
	if err := json.NewEncoder(body).Encode(data); err != nil {
		return errors.Wrap(err, "encoding payload")
	}

	return errors.Wrap(
		c.Request(ClientRequestOpts{
			AuthType: AuthTypeBearerToken,
			Body:     body,
			Context:  ctx,
			Method:   http.MethodPatch,
			OKStatus: http.StatusNoContent,
			URL:      fmt.Sprintf("https://api.twitch.tv/helix/channels?broadcaster_id=%s", broadcaster),
		}),
		"executing request",
	)
}

func (c *Client) RemoveChannelVIP(ctx context.Context, broadcasterName, userName string) error {
	broadcaster, err := c.GetIDForUsername(broadcasterName)
	if err != nil {
		return errors.Wrap(err, "getting ID for broadcaster name")
	}

	userID, err := c.GetIDForUsername(userName)
	if err != nil {
		return errors.Wrap(err, "getting ID for user name")
	}

	return errors.Wrap(
		c.Request(ClientRequestOpts{
			AuthType: AuthTypeBearerToken,
			Context:  ctx,
			Method:   http.MethodDelete,
			OKStatus: http.StatusNoContent,
			URL:      fmt.Sprintf("https://api.twitch.tv/helix/channels/vips?broadcaster_id=%s&user_id=%s", broadcaster, userID),
		}),
		"executing request",
	)
}

// RunCommercial starts a commercial on the specified channel
func (c *Client) RunCommercial(ctx context.Context, channel string, duration int64) error {
	channelID, err := c.GetIDForUsername(channel)
	if err != nil {
		return errors.Wrap(err, "getting ID for channel name")
	}

	payload := struct {
		BroadcasterID string `json:"broadcaster_id"`
		Length        int64  `json:"length"`
	}{
		BroadcasterID: channelID,
		Length:        duration,
	}

	body := new(bytes.Buffer)
	if err := json.NewEncoder(body).Encode(payload); err != nil {
		return errors.Wrap(err, "encoding payload")
	}

	return errors.Wrap(
		c.Request(ClientRequestOpts{
			AuthType: AuthTypeBearerToken,
			Body:     body,
			Context:  ctx,
			Method:   http.MethodPost,
			OKStatus: http.StatusOK,
			URL:      "https://api.twitch.tv/helix/channels/commercial",
		}),
		"executing request",
	)
}