package http

import (
	stdhttp "net/http"
	"regexp"
	"strings"

	"github.com/unkn0wn-root/resterm/internal/httpver"
	str "github.com/unkn0wn-root/resterm/internal/util"
)

var methodRe = regexp.MustCompile(
	`^(?i)(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE|CONNECT|WS|WSS)\b`,
)

func IsMethodLine(line string) bool {
	return methodRe.MatchString(line)
}

func ParseMethodLine(line string) (method string, url string, ver httpver.Version, ok bool) {
	if !IsMethodLine(line) {
		return "", "", httpver.Unknown, false
	}

	fields := strings.Fields(line)
	if len(fields) < 2 {
		return "", "", httpver.Unknown, false
	}

	method = str.UpperTrim(fields[0])
	if method == "WS" || method == "WSS" {
		method = stdhttp.MethodGet
	}

	urlFields, ver := httpver.SplitToken(fields[1:])
	if len(urlFields) == 0 {
		return "", "", httpver.Unknown, false
	}
	url = strings.Join(urlFields, " ")
	return method, url, ver, true
}

func ParseWebSocketURLLine(line string) (url string, ok bool) {
	trimmed := str.Trim(line)
	if trimmed == "" {
		return "", false
	}
	lower := str.LowerTrim(trimmed)
	if strings.HasPrefix(lower, "ws://") || strings.HasPrefix(lower, "wss://") {
		return trimmed, true
	}
	return "", false
}

type Builder struct {
	method       string
	url          string
	headers      stdhttp.Header
	headerDone   bool
	bodyLines    []string
	bodyFromFile string
	mimeType     string
}

func New() *Builder {
	return &Builder{}
}

func (b *Builder) HasMethod() bool {
	return b.method != ""
}

func (b *Builder) SetMethodAndURL(method, url string) {
	m := str.UpperTrim(method)
	if m == "WS" || m == "WSS" {
		m = stdhttp.MethodGet
	}
	b.method = m
	b.url = str.Trim(url)
}

func (b *Builder) Method() string {
	return b.method
}

func (b *Builder) URL() string {
	return b.url
}

func (b *Builder) Headers() stdhttp.Header {
	if b.headers == nil {
		b.headers = make(stdhttp.Header)
	}
	return b.headers
}

func (b *Builder) HeaderMap() stdhttp.Header {
	return b.headers
}

func (b *Builder) AddHeader(name, value string) {
	headers := b.Headers()
	headers.Add(name, value)
	if strings.EqualFold(name, "Content-Type") {
		b.mimeType = value
	}
}

func (b *Builder) HeaderDone() bool {
	return b.headerDone
}

func (b *Builder) MarkHeadersDone() {
	b.headerDone = true
}

func (b *Builder) AppendBodyLine(line string) {
	b.bodyLines = append(b.bodyLines, line)
}

func (b *Builder) SetBodyFromFile(path string) {
	b.bodyFromFile = str.Trim(path)
	b.bodyLines = nil
}

func (b *Builder) BodyFromFile() string {
	return b.bodyFromFile
}

func (b *Builder) BodyText() string {
	if len(b.bodyLines) == 0 {
		return ""
	}
	return strings.Join(b.bodyLines, "\n")
}

func (b *Builder) MimeType() string {
	return b.mimeType
}
