package dmapi

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"time"
)

type token string

const sessionIDKey token = "session-id"

// Token session ID.
// > Every request (except "login") requires the presence of the Auth-Sid variable ("Session ID"),
// > which is returned by the "login" request (login). An active session will expire after some inactivity period (default: 1 hour).
// https://joker.com/faq/content/22/12/en/commonalities-for-all-requests.html
type Token struct {
	SessionID string
	ExpireAt  time.Time
}

// login performs a log in to Joker's DMAPI.
func (c *Client) login(ctx context.Context) (*Response, error) {
	var values url.Values
	switch {
	case c.username != "" && c.password != "":
		values = url.Values{
			"username": {c.username},
			"password": {c.password},
		}
	case c.apiKey != "":
		values = url.Values{"api-key": {c.apiKey}}
	default:
		return nil, errors.New("no username and password or api-key")
	}

	response, err := c.postRequest(ctx, "login", values)
	if err != nil {
		return response, err
	}

	if response == nil {
		return nil, errors.New("login returned nil response")
	}

	if response.AuthSid == "" {
		return response, errors.New("login did not return valid Auth-Sid")
	}

	return response, nil
}

// Logout closes authenticated session with Joker's DMAPI.
func (c *Client) Logout(ctx context.Context) (*Response, error) {
	if c.token == nil {
		return nil, errors.New("already logged out")
	}

	response, err := c.postRequest(ctx, "logout", url.Values{})

	c.muToken.Lock()
	c.token = nil
	c.muToken.Unlock()

	if err != nil {
		return response, err
	}

	return response, nil
}

func (c *Client) CreateAuthenticatedContext(ctx context.Context) (context.Context, error) {
	c.muToken.Lock()
	defer c.muToken.Unlock()

	if c.token != nil && time.Now().UTC().Before(c.token.ExpireAt) {
		return context.WithValue(ctx, sessionIDKey, c.token.SessionID), nil
	}

	response, err := c.login(ctx)
	if err != nil {
		return nil, formatResponseError(response, err)
	}

	c.token = &Token{
		SessionID: response.AuthSid,
		ExpireAt:  time.Now().UTC().Add(1 * time.Hour),
	}

	return context.WithValue(ctx, sessionIDKey, response.AuthSid), nil
}

func getSessionID(ctx context.Context) string {
	tok, ok := ctx.Value(sessionIDKey).(string)
	if !ok {
		return ""
	}

	return tok
}

// formatResponseError formats error with optional details from DMAPI response.
func formatResponseError(response *Response, err error) error {
	if response != nil {
		return fmt.Errorf("joker: DMAPI error: %w Response: %v", err, response.Headers)
	}
	return fmt.Errorf("joker: DMAPI error: %w", err)
}