// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.

package resource

import (
	"context"
_	"errors"
	"fmt"
	"gopkg.in/yaml.v3"
	"io"
	"net/url"
	"net/http"
_	"os"
	"encoding/json"
  "strings"
	"log/slog"
	"gitea.rosskeen.house/rosskeen.house/machine"
)

func init() {
	ResourceTypes.Register("http", HTTPFactory)
	ResourceTypes.Register("https", HTTPFactory)
}

func HTTPFactory(u *url.URL) Resource {
	h := NewHTTP()
	h.Endpoint = u.String()
	return h
}

type HTTPHeader struct {
	Name string `yaml:"name" json:"name"`
	Value string `yaml:"value" json:"value"`
}

// Manage the state of an HTTP endpoint
type HTTP struct {
	client *http.Client `yaml:"-" json:"-"`
	Endpoint string `yaml:"endpoint" json:"endpoint"`
	Headers []HTTPHeader `yaml:"headers,omitempty" json:"headers,omitempty"`
	Body  string `yaml:"body,omitempty" json:"body,omitempty"`
	State string `yaml:"state,omitempty" json:"state,omitempty"`
}

func NewHTTP() *HTTP {
	return &HTTP{ client: &http.Client{} }
}

func (h *HTTP) Clone() Resource {
	return &HTTP {
		client: h.client,
		Endpoint: h.Endpoint,
		Headers: h.Headers,
		Body: h.Body,
		State: h.State,
	}
}

func (h *HTTP) StateMachine() machine.Stater {
	return StorageMachine()
}

func (h *HTTP) URI() string {
	return h.Endpoint
}

func (h *HTTP) SetURI(uri string) error {
	if _, e := url.Parse(uri); e != nil {
		return fmt.Errorf("%w: %s is not a file", ErrInvalidResourceURI, uri)
	}
	h.Endpoint = uri
	return nil
}

func (h *HTTP) JSON() ([]byte, error) {
	return json.Marshal(h)
}

func (h *HTTP) Validate() error {
	s := NewSchema(h.Type())
	jsonDoc, jsonErr := h.JSON()
	if jsonErr == nil {
		return s.Validate(string(jsonDoc))
	}
	return jsonErr
}

func (h *HTTP) Apply() error {
	switch h.State {
	case "absent":
	case "present":
	}
	_,e := h.Read(context.Background())
	if e == nil {
		h.State = "present"
	}
	return e
}

func (h *HTTP) Load(r io.Reader) error {
	c := NewYAMLDecoder(r)
	return c.Decode(h)
}

func (h *HTTP) LoadDecl(yamlResourceDeclaration string) error {
	slog.Info("LoadDecl()", "yaml", yamlResourceDeclaration)
	c := NewYAMLStringDecoder(yamlResourceDeclaration)
	return c.Decode(h)
}

func (h *HTTP) ResolveId(ctx context.Context) string {
	return h.Endpoint
}

func (h *HTTP) Create() error {
	body := strings.NewReader(h.Body)
	req, reqErr := http.NewRequest("POST", h.Endpoint, body)
	if reqErr != nil {
		return reqErr
	}
	for _,header := range h.Headers {
		req.Header.Add(header.Name, header.Value)
	}
	resp, err := h.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return err
}

func (h *HTTP) Read(ctx context.Context) ([]byte, error) {
	req, reqErr := http.NewRequestWithContext(ctx, "GET", h.Endpoint, nil)
	if reqErr != nil {
		return nil, reqErr
	}
	slog.Info("HTTP.Read() ", "request", req, "err", reqErr)

	if len(h.Headers) > 0 {
 	  for _,header := range h.Headers {
  		req.Header.Add(header.Name, header.Value)
  	}
  }

	resp, err := h.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, errReadBody := io.ReadAll(resp.Body)
	if errReadBody != nil {
		return nil, errReadBody
	}
	h.Body = string(body)
	return yaml.Marshal(h)
}

func (h *HTTP) Type() string {
	return "http"
}
