package patient

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

	cfg "simrs-vx/internal/infra/sync-cfg"
	pl "simrs-vx/pkg/logger"

	e "simrs-vx/internal/domain/main-entities/patient"
	un "simrs-vx/internal/use-case/simgos-sync-use-case/new/patient"

	d "github.com/karincake/dodol"
)

const source = "old-to-new-patient"

var path = "patient"

func Create(input e.Patient) (*d.Data, error) {
	evt := pl.Event{
		Feature: "Create",
		Source:  source,
	}

	pl.SetLogInfo(&evt, input, "started", "create")

	// create request body
	jsonPatient, err := json.Marshal(input)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}
	reqBody := bytes.NewBuffer(jsonPatient)
	// send data to main-api
	resp, err := send(http.MethodPost, path, reqBody, *input.RegisteredBy_User_Name)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}
	// getting response
	var data MainApiResp
	err = json.Unmarshal(resp, &data)
	if err != nil {
		return nil, pl.SetLogError(&evt, string(resp))
	}
	// create PatientLink
	un.CreateLinkData(data.Data.Id, input.Id, &evt)
	pl.SetLogInfo(&evt, nil, "complete")
	return &d.Data{
		Meta: d.II{
			"source":    source,
			"structure": "single-data",
			"status":    "created",
		},
		Data: input.ToResponse(),
	}, nil
}

func Update(input e.Patient) (*d.Data, error) {
	evt := pl.Event{
		Feature: "Update",
		Source:  source,
	}
	pl.SetLogInfo(&evt, input, "started", "update")

	// get patient link from database
	patientLink, err := ReadDetailLinkData(input.Id)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}

	// get data patient from main-api
	resp, err := send(http.MethodGet, fmt.Sprintf("%s%v", "patient/", patientLink.Simx_Id), nil, *input.RegisteredBy_User_Name)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}
	// unpack data from resp
	var respData MainApiResp
	err = json.Unmarshal(resp, &respData)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}

	// copy data from simgos m_patient to simx Patient
	preserve := map[string]bool{
		"Main.Id":   true,
		"Person.Id": true,
	}
	simxPatient := respData.Data
	SetPatient(&simxPatient, &input, preserve)

	jsonPatient, err := json.Marshal(simxPatient)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}

	// send data to main api
	url := fmt.Sprintf("%s%v", "patient/", simxPatient.Id)
	reqBody := bytes.NewBuffer(jsonPatient)
	_, err = send(http.MethodPatch, url, reqBody, *input.RegisteredBy_User_Name)
	if err != nil {
		return nil, pl.SetLogError(&evt, input)
	}

	pl.SetLogInfo(&evt, nil, "complete")
	return &d.Data{
		Meta: d.II{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
		Data: input.ToResponse(),
	}, nil
}

func Delete(input e.Patient) (*d.Data, error) {
	evt := pl.Event{
		Feature: "Delete",
		Source:  source,
	}
	pl.SetLogInfo(&evt, input, "started", "delete")

	// get patient link from database
	patientLink, err := ReadDetailLinkData(input.Id)
	if err != nil {
		return nil, err
	}
	_, err = send(http.MethodDelete, fmt.Sprintf("%s%v", "patient/", patientLink.Simx_Id), nil, *input.RegisteredBy_User_Name)
	if err != nil {
		return nil, err
	}
	return &d.Data{
		Meta: d.II{
			"source":    source,
			"structure": "single-data",
			"status":    "deleted",
		},
		Data: input.ToResponse(),
	}, nil
}

func send(method string, endpoint string, body *bytes.Buffer, username string) ([]byte, error) {
	var url string = cfg.O.NewHost + endpoint
	var reader io.Reader = nil
	if body != nil {
		reader = body
	}
	req, err := http.NewRequest(method, url, reader)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Sync-Source", cfg.O.OldSource)
	req.Header.Set("X-Sync-SecretKey", cfg.O.NewSecretKey)
	req.Header.Set("X-Sync-UserName", username)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	respBody, _ := io.ReadAll(resp.Body)
	return respBody, nil
}
