//go:generate ${GOBIN}/mockgen -destination oncall_mock.go -package=oncall ${GO_MODULE}/pkg/clients/oncall Interface
package oncall

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Interface is the read-only client for the aisupport oncall v2 service. It is
// used to look up who is currently on-call for a team, so jiraport can assign to
// the real current on-call engineer instead of round-robining a static category.
type Interface interface {
	// GetCurrentOnCall returns the current primary on-call engineer emails for
	// the given team ref (numeric team id or unique team slug name).
	GetCurrentOnCall(teamRef string) ([]string, error)
	// GetTeamManagers returns the manager emails of the given team ref. Used to
	// escalate to a team's leader.
	GetTeamManagers(teamRef string) ([]string, error)
}

type Client struct {
	client *http.Client
	addr   string
	apiKey string
}

// New builds an oncall client. addr is the service base url (e.g.
// https://oncall.example.com); apiKey is a service API key sent as a bearer
// token. A nil client (addr == "") is returned so callers can treat an
// unconfigured oncall service as "disabled".
func New(addr string, apiKey string) (Interface, error) {
	if addr == "" {
		return nil, nil
	}
	return &Client{
		client: &http.Client{Timeout: 10 * time.Second},
		addr:   addr,
		apiKey: apiKey,
	}, nil
}

type currentOnCallResp struct {
	TeamID                uint     `json:"team_id"`
	AtTime                int64    `json:"at_time"`
	CurrentEngineerEmails []string `json:"current_engineer_emails"`
	BackupEngineerEmails  []string `json:"backup_engineer_emails"`
	CurrentNoOnCall       bool     `json:"current_no_oncall"`
}

func (c *Client) GetCurrentOnCall(teamRef string) ([]string, error) {
	if teamRef == "" {
		return nil, fmt.Errorf("empty team ref")
	}
	endpoint := fmt.Sprintf("%s/api/v1/teams/%s/current-oncall", c.addr, url.PathEscape(teamRef))
	req, err := http.NewRequest(http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("fail to build current-oncall request, %w", err)
	}
	if c.apiKey != "" {
		req.Header.Set("Authorization", "Bearer "+c.apiKey)
	}

	res, err := c.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("fail to query current oncall, %w", err)
	}
	defer res.Body.Close()

	resBody, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("fail to read current-oncall response, %w", err)
	}
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("fail to query current oncall, status code: %d, message: %s", res.StatusCode, string(resBody))
	}

	var parsed currentOnCallResp
	if err := json.Unmarshal(resBody, &parsed); err != nil {
		return nil, fmt.Errorf("fail to unmarshal current-oncall response, %w", err)
	}
	return parsed.CurrentEngineerEmails, nil
}

type teamManagersResp struct {
	TeamID   uint `json:"team_id"`
	Managers []struct {
		EngineerEmail string `json:"engineer_email"`
	} `json:"managers"`
}

func (c *Client) GetTeamManagers(teamRef string) ([]string, error) {
	if teamRef == "" {
		return nil, fmt.Errorf("empty team ref")
	}
	endpoint := fmt.Sprintf("%s/api/v1/teams/%s/managers", c.addr, url.PathEscape(teamRef))
	req, err := http.NewRequest(http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("fail to build managers request, %w", err)
	}
	if c.apiKey != "" {
		req.Header.Set("Authorization", "Bearer "+c.apiKey)
	}

	res, err := c.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("fail to query team managers, %w", err)
	}
	defer res.Body.Close()

	resBody, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("fail to read managers response, %w", err)
	}
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("fail to query team managers, status code: %d, message: %s", res.StatusCode, string(resBody))
	}

	var parsed teamManagersResp
	if err := json.Unmarshal(resBody, &parsed); err != nil {
		return nil, fmt.Errorf("fail to unmarshal managers response, %w", err)
	}
	out := make([]string, 0, len(parsed.Managers))
	for _, m := range parsed.Managers {
		if e := strings.TrimSpace(m.EngineerEmail); e != "" {
			out = append(out, e)
		}
	}
	return out, nil
}
