package web

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

var caser = cases.Title(language.Und)

type Reply struct {
	Code     int
	buffer   io.ReadWriter
	headers  map[string]string
	cookies  []*http.Cookie
	renderer Renderer
}

func NewReply(renderer Renderer) *Reply {
	s := new(Reply)
	s.headers = make(map[string]string)
	s.buffer = bytes.NewBuffer(nil)
	s.renderer = renderer
	return s
}

func (s *Reply) SetHeader(name, value string) {
	s.headers[caser.String(name)] = value
}

func (s *Reply) Header(name string) string {
	return s.headers[caser.String(name)]
}

func (s *Reply) GoTo(location string) {
	s.Code = 302
	s.headers["Location"] = location
}

func (s *Reply) RedirectTo(u *Location) {
	s.Code = 302
	s.headers["Location"] = u.String()
}

func (s *Reply) SetCookie(cookie *http.Cookie) {
	s.cookies = append(s.cookies, cookie)
}

func (s *Reply) Write(data []byte) (int, error) {
	return s.buffer.Write(data)
}

func (s *Reply) WriteString(data string) (int, error) {
	return s.buffer.Write([]byte(data))
}

func (s *Reply) AsJSON(v any) (err error) {
	buf, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("json.Marshal: %s", err)
	}
	s.headers["Content-Type"] = "application/json; charset=utf-8"
	_, err = s.buffer.Write(buf)
	if err != nil {
		return fmt.Errorf("Write: %s", err)
	}
	return
}

func (s *Reply) Render(template string, data any) (err error) {
	if s.renderer == nil {
		return errors.New("renderer is not set")
	}
	return s.renderer.Render(s.buffer, template, data)
}

func (s *Reply) AsFile(filename, mimeType string, buf *bytes.Buffer) {
	s.headers["Content-Type"] = mimeType
	s.headers["Content-Disposition"] = fmt.Sprintf(`attachment; filename="%s"`, filename)
	s.buffer = buf
}

type Location struct {
	baseURL string
	values  url.Values
}

func NewLocation(baseURL string) *Location {
	s := new(Location)
	s.baseURL = baseURL
	s.values = url.Values{}
	return s
}

func (s *Location) Add(name string, value any) {
	s.values.Add(name, fmt.Sprintf("%s", value))
}

func (s *Location) String() string {
	return s.baseURL + "?" + s.values.Encode()
}
