// Package ghssh provides a cache for github ssh keys.
// It fetches the keys from github and caches them on disk.
package ghssh

import (
	"bufio"
	"context"
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/gopasspw/gopass/pkg/debug"
)

var httpClient = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			// enforce TLS 1.3
			MinVersion: tls.VersionTLS13,
		},
	},
}

var baseURL = "https://github.com"

// ListKeys returns the public keys for a github user. It will
// cache results up to a configurable amount of time (default: 6h).
func (c *Cache) ListKeys(ctx context.Context, user string) ([]string, error) {
	pk, err := c.disk.Get(user)
	if err != nil {
		debug.Log("failed to fetch %s from cache: %s", user, err)
	}

	if len(pk) > 0 {
		return pk, nil
	}

	keys, err := c.fetchKeys(ctx, user)
	if err != nil {
		return nil, err
	}

	if len(keys) < 1 {
		return nil, fmt.Errorf("key not found")
	}

	_ = c.disk.Set(user, keys)

	return keys, nil
}

// fetchKeys returns the public keys for a github user.
func (c *Cache) fetchKeys(ctx context.Context, user string) ([]string, error) {
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	url := fmt.Sprintf("%s/%s.keys", baseURL, user)
	debug.Log("fetching public keys for %s from github: %s", user, url)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() //nolint:errcheck

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to fetch keys from %s: %s", url, resp.Status)
	}

	out := make([]string, 0, 5)
	scanner := bufio.NewScanner(resp.Body)

	for scanner.Scan() {
		out = append(out, strings.TrimSpace(scanner.Text()))
	}

	return out, nil
}
