// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

// Package client provides access token management and Lunatask API client creation.
package client

import (
	"errors"
	"fmt"
	"os"
	"runtime/debug"

	"git.secluded.site/go-lunatask"
	"github.com/zalando/go-keyring"
)

const (
	keyringService = "lunatask-mcp-server"
	keyringUser    = "access-token"

	// EnvAccessToken is the environment variable for the access token.
	EnvAccessToken = "LUNATASK_ACCESS_TOKEN"
)

// TokenSource indicates where the access token was loaded from.
type TokenSource int

// Token source constants.
const (
	TokenSourceNone    TokenSource = iota // No token found
	TokenSourceEnv                        // From environment variable
	TokenSourceKeyring                    // From system keyring
)

func (s TokenSource) String() string {
	switch s {
	case TokenSourceEnv:
		return "environment variable"
	case TokenSourceKeyring:
		return "system keyring"
	case TokenSourceNone:
		return "none"
	default:
		return "none"
	}
}

// ErrNoToken indicates no access token is available.
var ErrNoToken = errors.New(
	"no access token found; run 'lunatask-mcp-server config' to configure, " +
		"or set LUNATASK_ACCESS_TOKEN",
)

// New creates a Lunatask client using the access token from available sources.
// Token priority: environment variable > system keyring.
func New() (*lunatask.Client, error) {
	token, _, err := GetToken()
	if err != nil {
		return nil, err
	}

	return lunatask.NewClient(token, lunatask.UserAgent("lunatask-mcp-server/"+version())), nil
}

// GetToken returns the access token and its source.
// Token priority: environment variable > system keyring.
// Returns ErrNoToken if no token is found.
func GetToken() (string, TokenSource, error) {
	// 1. Environment variable (highest priority for container deployments)
	if token := os.Getenv(EnvAccessToken); token != "" {
		return token, TokenSourceEnv, nil
	}

	// 2. System keyring
	token, err := keyring.Get(keyringService, keyringUser)
	if err == nil && token != "" {
		return token, TokenSourceKeyring, nil
	}

	if err != nil && !errors.Is(err, keyring.ErrNotFound) {
		return "", TokenSourceNone, fmt.Errorf("accessing system keyring: %w", err)
	}

	return "", TokenSourceNone, ErrNoToken
}

// HasKeyringToken checks if an access token is stored in the keyring.
// Returns (true, nil) if found, (false, nil) if not found,
// or (false, error) if there was a keyring access problem.
func HasKeyringToken() (bool, error) {
	_, err := keyring.Get(keyringService, keyringUser)
	if err != nil {
		if errors.Is(err, keyring.ErrNotFound) {
			return false, nil
		}

		return false, fmt.Errorf("accessing system keyring: %w", err)
	}

	return true, nil
}

// SetKeyringToken stores the access token in the system keyring.
func SetKeyringToken(token string) error {
	if err := keyring.Set(keyringService, keyringUser, token); err != nil {
		return fmt.Errorf("saving to keyring: %w", err)
	}

	return nil
}

// DeleteKeyringToken removes the access token from the system keyring.
func DeleteKeyringToken() error {
	if err := keyring.Delete(keyringService, keyringUser); err != nil {
		if errors.Is(err, keyring.ErrNotFound) {
			return nil
		}

		return fmt.Errorf("deleting from keyring: %w", err)
	}

	return nil
}

// HasEnvToken checks if an access token is set via environment variable.
func HasEnvToken() bool {
	return os.Getenv(EnvAccessToken) != ""
}

// version returns the module version from build info, or "dev" if unavailable.
func version() string {
	info, ok := debug.ReadBuildInfo()
	if !ok || info.Main.Version == "" || info.Main.Version == "(devel)" {
		return "dev"
	}

	return info.Main.Version
}
