package client

import (
	"encoding/json"
	"fmt"
	"ips-lacpass-backend/pkg/errors"
	"ips-lacpass-backend/pkg/utils"
	"net/http"
)

type ClientInterface interface {
	GetDocumentReference(identifier string) (*Bundle, error)
	GetIpsBundle(url string) (map[string]interface{}, error)
	GetIpsICVP(idBundle string, immunizationId *string) (string, error)
}

type IpsClient struct {
	Client          *http.Client
	BaseURL         string
	MediatorBaseURL string
}

func NewClient(baseURL string, mediatorBaseURL string) IpsClient {
	return IpsClient{
		Client:          &http.Client{},
		BaseURL:         baseURL,
		MediatorBaseURL: mediatorBaseURL,
	}
}

func request(client *http.Client, req *http.Request) (*http.Response, error) {
	resp, err := client.Do(req)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: 502,
			Body:       []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to FHIR service"}},
			Err:        fmt.Errorf("failed to send request: %w", err),
		}
	}

	if resp.StatusCode != http.StatusOK {
		return nil, &errors.HttpError{
			StatusCode: resp.StatusCode,
			Body:       []map[string]interface{}{{"error": "fhir_error", "message": resp.Body}},
		}
	}
	return resp, nil
}

func (c *IpsClient) GetDocumentReference(identifier string) (*Bundle, error) {
	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/DocumentReference?identifier=%s", c.BaseURL, identifier), nil)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}},
			Err:        err,
		}
	}
	req.Header.Set("Accept", "application/fhir+json, application/json")

	resp, err := request(c.Client, req)
	if err != nil {
		return nil, err
	}
	defer utils.CloseBody(resp.Body)

	var bundle Bundle
	if err := json.NewDecoder(resp.Body).Decode(&bundle); err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode DocumentReference bundle"}},
			Err:        fmt.Errorf("failed to decode DocumentReference bundle: %w", err),
		}
	}

	return &bundle, nil
}

func (c *IpsClient) GetIpsBundle(url string) (map[string]interface{}, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}},
			Err:        err,
		}
	}
	req.Header.Set("Accept", "application/fhir+json, application/json")

	resp, err := request(c.Client, req)
	if err != nil {
		return nil, err
	}
	defer utils.CloseBody(resp.Body)

	var bundle map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&bundle); err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode IPS bundle"}},
			Err:        fmt.Errorf("failed to decode IPS bundle: %w", err),
		}
	}

	return bundle, nil
}

func (c *IpsClient) GetIpsICVP(idBundle string, immunizationId *string) (string, error) {
	// TODO: To be implemented by the participant
	return "", &errors.HttpError{
		StatusCode: 500,
		Body:       []map[string]interface{}{{"error": "Not implemented error", "message": "this method is not implemented yet"}},
		Err:        fmt.Errorf("failed to get document reference"),
	}
}
