package gemini

import (
	"bufio"
	"errors"
	"io"
	"net/url"
	"strconv"
	"strings"

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

// InvalidRequestLineEnding indicates that a gemini request didn't end with "\r\n".
var InvalidRequestLineEnding = errors.New("invalid request line ending")

// ParseRequest parses a single gemini/titan request from a reader.
//
// If the reader argument is a *bufio.Reader, it will only read a single line from it.
func ParseRequest(rdr io.Reader) (*types.Request, error) {
	bufrdr, ok := rdr.(*bufio.Reader)
	if !ok {
		bufrdr = bufio.NewReader(rdr)
	}

	line, err := bufrdr.ReadString('\n')
	if err != io.EOF && err != nil {
		return nil, err
	}

	if len(line) < 2 || line[len(line)-2:] != "\r\n" {
		return nil, InvalidRequestLineEnding
	}

	u, err := url.Parse(line[:len(line)-2])
	if err != nil {
		return nil, err
	}

	if u.Scheme == "" {
		u.Scheme = "gemini"
	}

	req := &types.Request{URL: u}

	if u.Scheme == "titan" {
		length, err := sizeParam(u.Path)
		if err != nil {
			return nil, err
		}
		req.Meta = io.LimitReader(bufrdr, int64(length))
	}

	return req, nil
}

// GetTitanRequestBody fetches the request body from a titan request.
//
// It returns nil if the argument is not a titan request or it otherwise
// does not have a request body set.
func GetTitanRequestBody(request *types.Request) io.Reader {
	if request.Scheme != "titan" {
		return nil
	}
	if rdr, ok := request.Meta.(io.Reader); ok {
		return rdr
	}
	return nil
}

func sizeParam(path string) (int, error) {
	_, rest, found := strings.Cut(path, ";")
	if !found {
		return 0, errors.New("no params in titan request path")
	}

	for _, piece := range strings.Split(rest, ";") {
		key, val, _ := strings.Cut(piece, "=")
		if key == "size" {
			return strconv.Atoi(val)
		}
	}

	return 0, errors.New("no size param found in titan request")
}