package credentials

import "time"

type AuthType string

const (
	AuthPassword AuthType = "password"
	AuthOAuth2   AuthType = "oauth2"
)

// Credential is the decrypted mail account secret material.
type Credential struct {
	AuthType       AuthType
	Username       string
	Password       string
	AccessToken    string
	RefreshToken   string
	Expiry         time.Time
	OAuthProvider  string // google | microsoft
}

func (c Credential) IsOAuth() bool {
	return c.AuthType == AuthOAuth2
}

func (c Credential) NeedsRefresh() bool {
	if !c.IsOAuth() || c.RefreshToken == "" {
		return false
	}
	if c.Expiry.IsZero() {
		return false
	}
	return time.Now().Add(2 * time.Minute).After(c.Expiry)
}

type storedPayload struct {
	AuthType      string `json:"auth_type,omitempty"`
	Username      string `json:"username"`
	Password      string `json:"password,omitempty"`
	AccessToken   string `json:"access_token,omitempty"`
	RefreshToken  string `json:"refresh_token,omitempty"`
	Expiry        string `json:"expiry,omitempty"`
	OAuthProvider string `json:"oauth_provider,omitempty"`
}

func (c Credential) toStored() storedPayload {
	p := storedPayload{
		AuthType:      string(c.AuthType),
		Username:      c.Username,
		Password:      c.Password,
		AccessToken:   c.AccessToken,
		RefreshToken:  c.RefreshToken,
		OAuthProvider: c.OAuthProvider,
	}
	if !c.Expiry.IsZero() {
		p.Expiry = c.Expiry.UTC().Format(time.RFC3339)
	}
	if p.AuthType == "" {
		p.AuthType = string(AuthPassword)
	}
	return p
}

func storedToCredential(p storedPayload) (Credential, error) {
	authType := AuthType(p.AuthType)
	if authType == "" {
		authType = AuthPassword
	}
	c := Credential{
		AuthType:      authType,
		Username:      p.Username,
		Password:      p.Password,
		AccessToken:   p.AccessToken,
		RefreshToken:  p.RefreshToken,
		OAuthProvider: p.OAuthProvider,
	}
	if p.Expiry != "" {
		t, err := time.Parse(time.RFC3339, p.Expiry)
		if err != nil {
			return Credential{}, err
		}
		c.Expiry = t
	}
	if c.Username == "" {
		return Credential{}, errIncomplete
	}
	switch authType {
	case AuthPassword:
		if c.Password == "" {
			return Credential{}, errIncomplete
		}
	case AuthOAuth2:
		if c.AccessToken == "" {
			return Credential{}, errIncomplete
		}
	default:
		return Credential{}, errUnsupportedAuth
	}
	return c, nil
}
