package sssmgr

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

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
	"github.com/gorilla/mux"
)

type SssMgrHttp struct {
	notify func(map[string]interface{})
}

const (
	headerAccept      = "application/json"
	headerContentType = "application/json"
)

func NewSssMgrHttp() (http_mgr *SssMgrHttp) {
	log.Info(">>> NewSssMgrHttp")
	return new(SssMgrHttp)
}

func (http_mgr *SssMgrHttp) init(tm *SssMgr, notify func(map[string]interface{})) (err error) {
	log.Info(">>> init")

	http_mgr.notify = notify

	go func() {
		log.Info("Init: Starting OneM2M Notification server")
		http.HandleFunc("/", http_mgr.handleRoot)
		err := http.ListenAndServe(":31122", nil)
		if err != nil {
			log.Error(err.Error())
			return
		}
		log.Info("<<< Init: Terminating OneM2M Notification server")
	}()

	return nil
}

func (http_mgr *SssMgrHttp) handleRoot(w http.ResponseWriter, r *http.Request) {
	log.Debug(">>> handleRoot: ", r)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	// Get URL
	u, _ := url.Parse(r.URL.String())
	log.Info("url: ", u.RequestURI())

	if r.Method == "POST" { // Notification
		// Extract body
		bodyBytes, _ := ioutil.ReadAll(r.Body)
		log.Info("subscriptionsPost: bodyBytes: ", string(bodyBytes))
		var body map[string]map[string]interface{}
		err := json.Unmarshal(bodyBytes, &body)
		if err != nil {
			log.Error(err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err.Error())
			return
		}
		log.Info("handleRoot: body: ", body)
		//map[m2m:sgn:map[nev:map[net:3 rep:map[m2m:cnt:map[cbs:0 cni:0 ct:20250327T111057,824274 et:20300326T111032,133324 lt:20250327T111057,824274 mbs:10000 mni:10 pi:C7feaadbb0400 ri:cnt8465776292050557472 rn:test st:0 ty:3]]] sur:/laboai-acme-ic-cse/sub3916638126520907910]]
		if _, ok := body["m2m:sgn"]; !ok {
			err := errors.New("Only m2m:sgn is expected")
			log.Error(err.Error())
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprint(w, err.Error())
			return
		}
		// Call callback
		m := body["m2m:sgn"]
		log.Info("handleRoot: m: ", m)
		if http_mgr.notify != nil {
			http_mgr.notify(m)
		}
	} else {
		log.Warn("handleRoot: Unsupported method", r.Method)
		w.WriteHeader(http.StatusNotImplemented)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (http_mgr *SssMgrHttp) uninit() (err error) {
	log.Info(">>> uninit")

	return nil
}

func (http_mgr *SssMgrHttp) send(p_ctx SssMgrBindingProtocolContext) (err error, resp interface{}) {
	log.Info(">>> send: ", p_ctx)

	// Build the headers
	var headers = http.Header{}
	headers["Accept"] = []string{headerAccept}
	headers["X-M2M-Origin"] = []string{"C" + p_ctx.from}
	headers["X-M2M-RI"] = []string{p_ctx.rqi}
	headers["X-M2M-RVI"] = p_ctx.rvi
	s := headerContentType
	if p_ctx.ty != -1 {
		s = s + ";ty=" + strconv.Itoa(p_ctx.ty)
	}
	headers["Content-Type"] = []string{s}
	// Build the url
	url := "http://" + p_ctx.host + ":" + strconv.Itoa(p_ctx.port) + "/" + p_ctx.to
	// Set the method
	method := ""
	if p_ctx.op == 1 {
		method = "POST"
	} else if p_ctx.op == 2 {
		method = "GET"
	} else if p_ctx.op == 3 {
		method = "PATCH"
	} else if p_ctx.op == 4 {
		method = "DELETE"
	} else {
		err := errors.New("Invalid p_ctx.op")
		log.Error("send: ", err.Error())
		return err, nil
	}
	// Finalize the body
	log.Debug("send: url=", url)
	var response []byte
	if p_ctx.body != nil { // With body
		log.Debug("send: p_ctx.body=", p_ctx.body)
		body, err := json.Marshal(p_ctx.body)
		if err != nil {
			log.Error("send: ", err.Error())
			return err, nil
		}
		log.Debug("send: Request body: ", string(body))
		response, err = sendRequest(method, url, headers, bytes.NewBuffer(body), nil, p_ctx.queries, 201)
		if err != nil {
			log.Error("send: ", err.Error())
			return err, nil
		}
	} else { // Without body
		response, err = sendRequest(method, url, headers, nil, nil, p_ctx.queries, p_ctx.code)
		if err != nil {
			log.Error("send: ", err.Error())
			return err, nil
		}
	}
	log.Debug("send: response: ", string(response))
	if len(response) != 0 {
		err = json.Unmarshal(response, &resp)
		if err != nil {
			log.Error("send: ", err.Error())
			return err, nil
		}
		log.Debug("send: response: ", resp)
		log.Debug("send: TypeOf(response): ", reflect.TypeOf(resp))
	}

	return nil, resp
}
func sendRequest(method string, url string, headers http.Header, body io.Reader, vars map[string]string, query map[string]string, code int) ([]byte, error) {
	//log.Debug(">>> sendRequest: url: ", url)
	//log.Debug(">>> sendRequest: headers: ", headers)

	req, err := http.NewRequest(method, url, body)
	if err != nil || req == nil {
		return nil, err
	}
	if vars != nil {
		req = mux.SetURLVars(req, vars)
	}
	if query != nil {
		q := req.URL.Query()
		for k, v := range query {
			q.Add(k, v)
		}
		req.URL.RawQuery = q.Encode()
	}
	req.Header = headers
	req.Close = true

	//log.Debug("sendRequest: req: ", req)
	rr, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	// Check the status code is what we expect.
	//log.Debug("sendRequest: rr: ", rr)
	if status := rr.StatusCode; status != code {
		s := fmt.Sprintf("Wrong status code - got %v want %v", status, code)
		return nil, errors.New(s)
	}
	responseData, err := ioutil.ReadAll(rr.Body)
	if err != nil {
		return nil, err
	}
	//log.Debug("sendRequest: responseData: ", responseData)

	return responseData, nil
}
