// Copyright 2023 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0

package jobqueue

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"sync"
)

func NewHTTPRunnableFromMap(data map[string]interface{}) (*HTTPRunnable, error) {
	url, ok := data["url"].(string)
	if !ok {
		return nil, fmt.Errorf("%w: Invalid URL: %v", ErrInvalidData, data["url"])
	}

	method, ok := data["method"].(string)
	if !ok {
		return nil, fmt.Errorf("%w: Invalid Method: %v", ErrInvalidData, data["method"])
	}

	header, ok := data["header"].(map[string]string)
	if !ok {
		return nil, fmt.Errorf("%w: Invalid Header: %v", ErrInvalidData, data["header"])
	}

	body, ok := data["body"].(string)
	if !ok {
		return nil, fmt.Errorf("%w: Invalid Body: %v", ErrInvalidData, data["body"])
	}

	return &HTTPRunnable{
		URL:    url,
		Method: method,
		Header: header,
		Body:   body,
	}, nil
}

// HTTPResult is a result of a http request
type HTTPResult struct {
	StatusCode int
	Body       string
}

func (h *HTTPResult) GetResult() string {
	return fmt.Sprintf("StatusCode: %d\n\n%s", h.StatusCode, h.Body)
}

func (h *HTTPResult) GetError() (string, int) {
	if h.StatusCode >= 400 {
		return fmt.Sprintf("StatusCode: %d\n\n%s", h.StatusCode, h.Body), h.StatusCode
	}
	return "", 0
}

type HTTPRunnable struct {
	URL    string
	Method string
	Header map[string]string
	Body   string
	mu     sync.Mutex
}

func (h *HTTPRunnable) Run(ctx context.Context) (RunResult[HTTPResult], error) {
	client := &http.Client{}

	reqBody := bytes.NewBufferString(h.Body)
	req, err := http.NewRequestWithContext(ctx, h.Method, h.URL, reqBody)
	if err != nil {
		return RunResult[HTTPResult]{Status: ResultStatusFailed}, err
	}

	for key, value := range h.Header {
		req.Header.Set(key, value)
	}

	resp, err := client.Do(req)
	if err != nil {
		return RunResult[HTTPResult]{Status: ResultStatusFailed}, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return RunResult[HTTPResult]{Status: ResultStatusFailed}, err
	}

	return RunResult[HTTPResult]{
		Status: ResultStatusSuccess,
		Data: HTTPResult{
			StatusCode: resp.StatusCode,
			Body:       string(body),
		},
	}, nil
}

func (h *HTTPRunnable) GetType() string {
	return "http"
}

func (h *HTTPRunnable) GetPersistence() RunnableImport {
	h.mu.Lock()
	defer h.mu.Unlock()

	data := JSONMap{
		"url":    h.URL,
		"method": h.Method,
		"header": h.Header,
		"body":   h.Body,
	}

	return RunnableImport{
		Type: h.GetType(),
		Data: data,
	}
}