package client

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

type VhlClient struct {
	Client           *http.Client
	BaseURL          string
	ICVPValidatorUrl string
}

func NewClient(baseURL string, icvpValidatorUrl string) VhlClient {
	return VhlClient{
		Client:           &http.Client{},
		BaseURL:          baseURL,
		ICVPValidatorUrl: icvpValidatorUrl,
	}
}

func (c *VhlClient) CreateQr(ctx context.Context, body CreateQrRequest) (*QrData, error) {
	// TODO: To be implemented by the participant
	resp, err := c.Client.Post(fmt.Sprintf("%s/v2/vshcIssuance", c.BaseURL), "application/json", bytes.NewBuffer(body))
	if resp.StatusCode != http.StatusOK {
		return nil, &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"),
		}
	}
	return resp, nil
}

func (c *VhlClient) Validate(ctx context.Context, qrData string) (*QRValidationResponse, error) {
	body, err := json.Marshal(QrValidationRequest{QRCodeContent: qrData})
	if err != nil {
		return nil, fmt.Errorf("failed to marshal validation payload: %w", err)
	}
	resp, err := c.Client.Post(fmt.Sprintf("%s/v2/vshcValidation", c.BaseURL), "application/json", bytes.NewBuffer(body))
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusBadGateway,
			Body:       []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}},
			Err:        err,
		}
	}
	defer utils.CloseBody(resp.Body)

	if resp.StatusCode != http.StatusOK {
		bodyBytes, readErr := io.ReadAll(resp.Body)
		if readErr != nil {
			return nil, &errors.HttpError{
				StatusCode: resp.StatusCode,
				Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
				Err:        readErr,
			}
		}
		return nil, &errors.HttpError{
			StatusCode: resp.StatusCode,
			Body:       []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}},
			Err:        fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)),
		}
	}

	var valResp QRValidationResponse
	if err := json.NewDecoder(resp.Body).Decode(&valResp); err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode response"}},
			Err:        err,
		}
	}
	return &valResp, nil
}

func (c *VhlClient) GetIpsUrl(ctx context.Context, shLink string, passCode string) (*VhlManifestResponse, error) {
	u, err := url.Parse(fmt.Sprintf("%s/ips-url", c.BaseURL))
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}},
			Err:        err,
		}
	}

	q := u.Query()
	q.Set("shLink", shLink)
	q.Set("passCode", passCode)
	u.RawQuery = q.Encode()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), 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/json")

	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusBadGateway,
			Body:       []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}},
			Err:        err,
		}
	}
	defer utils.CloseBody(resp.Body)

	if resp.StatusCode != http.StatusOK {
		bodyBytes, readErr := io.ReadAll(resp.Body)
		if readErr != nil {
			return nil, &errors.HttpError{
				StatusCode: resp.StatusCode,
				Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
				Err:        readErr,
			}
		}
		return nil, &errors.HttpError{
			StatusCode: resp.StatusCode,
			Body:       []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}},
			Err:        fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)),
		}
	}

	var manifest VhlManifestResponse
	if err := json.NewDecoder(resp.Body).Decode(&manifest); err != nil {
		return nil, &errors.HttpError{
			StatusCode: http.StatusInternalServerError,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode manifest response"}},
			Err:        err,
		}
	}
	return &manifest, nil
}

func (c *VhlClient) ICVPValidate(ctx context.Context, qrData string) (*ICVPQRValidationResponse, error) {
	r := ICVPQrValidationRequest{
		IncludeRaw: true,
		QRData:     qrData,
	}
	body, err := json.Marshal(r)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal user payload: %w", err)
	}

	vu := fmt.Sprintf("%s/decode/hcert", c.ICVPValidatorUrl)
	req, err := http.NewRequest("POST", vu, bytes.NewBuffer(body))
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: 500,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}},
			Err:        err,
		}
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Client.Do(req)

	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: 502,
			Body:       []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}},
			Err:        err,
		}
	}
	defer utils.CloseBody(resp.Body)

	if resp.StatusCode != http.StatusOK {
		bodyBytes, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, &errors.HttpError{
				StatusCode: resp.StatusCode,
				Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
				Err:        err,
			}
		}
		return nil, &errors.HttpError{
			StatusCode: resp.StatusCode,
			Body:       []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}},
			Err:        fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)),
		}
	}

	bb, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: 500,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
			Err:        err,
		}
	}
	var valResp ICVPQRValidationResponse
	err = json.Unmarshal(bb, &valResp)
	if err != nil {
		return nil, &errors.HttpError{
			StatusCode: 500,
			Body:       []map[string]interface{}{{"error": "internal_error", "message": "Failed to read parse response body"}},
			Err:        err,
		}
	}
	return &valResp, nil
}
