package gopher

import (
	"bytes"
	"errors"
	"io"
	"net/url"
	"path"
	"strings"

	"tildegit.org/tjp/sliderule/internal/types"
)

// ParseRequest parses a gopher protocol request into a sliderule.Request object.
func ParseRequest(rdr io.Reader) (*types.Request, error) {
	selector, search, err := readFullRequest(rdr)
	if err != nil {
		return nil, err
	}

	if !strings.HasPrefix(selector, "/") {
		selector = "/" + selector
	}

	return &types.Request{
		URL: &url.URL{
			Scheme:   "gopher",
			Path:     path.Clean(selector),
			OmitHost: true, //nolint:typecheck
			// (for some reason typecheck on drone-ci doesn't realize OmitHost is a field in url.URL)
			RawQuery: url.QueryEscape(search),
		},
	}, nil
}

func readFullRequest(rdr io.Reader) (string, string, error) {
	// The vast majority of requests will fit in this size:
	// the specified 255 byte max for selector, then CRLF.
	buf := make([]byte, 257)

	n, err := rdr.Read(buf)
	if err != nil && !errors.Is(err, io.EOF) {
		return "", "", err
	}
	buf = buf[:n]

	// Full-text search transactions are the exception, they
	// may be longer because there is an additional search string
	if n == 257 && buf[256] != '\n' {
		intake := buf[n:cap(buf)]
		total := n
		for {
			intake = append(intake, 0)
			intake = intake[:cap(intake)]

			n, err = rdr.Read(intake)
			if err != nil && err != io.EOF {
				return "", "", err
			}
			total += n

			if n < cap(intake) || intake[cap(intake)-1] == '\n' {
				break
			}
			intake = intake[n:]
		}
		buf = buf[:total]
	}

	selector, search, _ := bytes.Cut(buf, []byte{'\t'})
	return strings.TrimRight(string(selector), "\r\n"), strings.TrimRight(string(search), "\r\n"), nil
}