package services

import (
	"errors"
	"net/http"
	"net/url"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/httpclient"
	"github.com/fiskerinc/cloud-services/pkg/logger"
)

// Not mockable
func NewOtaService(url string, token string) OtaServiceInterface {
	return &OtaService{
		otaURL:      url,
		otaAPIToken: token,
	}
}

type OtaServiceInterface interface {
	FlashpackVersionAdd(req common.CarFlashpackVersionAddRequest) (*http.Response, error)
	FlashpackVersionDelete(req common.CarFlashpackVersionRequest) (*http.Response, error)
	UpdateManifestSUMSAdd(req common.SUMSVersionCreate) (*http.Response, error)
	UpdateManifestSUMSDelete(version string) (*http.Response, error)
	UpdateManifestSUMSRxSwinsAdd(version string, req common.SwVersionRxSwinCreate) (*http.Response, error)
	UpdateManifestSUMSRxSwinsDelete(version string, rxswin string) (*http.Response, error)
}

type OtaService struct {
	otaURL      string
	otaAPIToken string
}

func (o *OtaService) FlashpackVersionAdd(req common.CarFlashpackVersionAddRequest) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "flashpack_version")
	if err != nil {
		return nil, err
	}

	postHeader := http.Header{}
	postHeader.Add("Api-Key", o.otaAPIToken)
	postHeader.Add("accept", "application/json")
	postHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Post(url, req, postHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to migrate flashpack version ECU mappings:\n Status: %s\n Model: %s Year: %d Flashpack: %s\n", resp.Status, req.CarModel, req.CarYear, req.Flashpack)
	}

	return resp, nil
}

func (o *OtaService) FlashpackVersionDelete(req common.CarFlashpackVersionRequest) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "flashpack_version")
	if err != nil {
		return nil, err
	}

	deleteHeader := http.Header{}
	deleteHeader.Add("Api-Key", o.otaAPIToken)
	deleteHeader.Add("accept", "application/json")
	deleteHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Delete(url, req, deleteHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to delete flashpack version ECU mappings:\n Status: %s\n Model: %s Year: %d Flashpack: %s\n", resp.Status, req.CarModel, req.CarYear, req.Flashpack)
	}

	return resp, nil
}

func (o *OtaService) UpdateManifestSUMSAdd(req common.SUMSVersionCreate) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "manifest/sums")
	if err != nil {
		return nil, err
	}

	if len(req.SUMSVersions) < 1 {
		return nil, errors.New("cannot call /manifest/sums/ POST with no sums versions to add")
	}

	postHeader := http.Header{}
	postHeader.Add("Api-Key", o.otaAPIToken)
	postHeader.Add("accept", "application/json")
	postHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Post(url, req, postHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to migrate SUMS:\n Status: %s\n Version: %s\n", resp.Status, req.SUMSVersions[0].Version)
	}

	return resp, nil
}

func (o *OtaService) UpdateManifestSUMSDelete(version string) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "manifest/sums/"+url.PathEscape(version))
	if err != nil {
		return nil, err
	}

	deleteHeader := http.Header{}
	deleteHeader.Add("Api-Key", o.otaAPIToken)
	deleteHeader.Add("accept", "application/json")
	deleteHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Delete(url, nil, deleteHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to delete SUMS:\n Status: %s\n Version: %s\n", resp.Status, version)
	}

	return resp, nil
}

func (o *OtaService) UpdateManifestSUMSRxSwinsAdd(version string, req common.SwVersionRxSwinCreate) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "/manifest/sums/"+url.PathEscape(version)+"/rxswins")
	if err != nil {
		return nil, err
	}

	postHeader := http.Header{}
	postHeader.Add("Api-Key", o.otaAPIToken)
	postHeader.Add("accept", "application/json")
	postHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Post(url, req, postHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to add RXSWINs to SUMS:\n Status: %s\n Version: %s\n", resp.Status, version)
	}

	return resp, nil
}

func (o *OtaService) UpdateManifestSUMSRxSwinsDelete(version string, rxswin string) (*http.Response, error) {
	url, err := url.JoinPath(o.otaURL, "manifest/sums/"+url.PathEscape(version)+"/rxswins/"+url.PathEscape(rxswin))
	if err != nil {
		return nil, err
	}

	deleteHeader := http.Header{}
	deleteHeader.Add("Api-Key", o.otaAPIToken)
	deleteHeader.Add("accept", "application/json")
	deleteHeader.Add("Content-Type", "application/json")

	resp, err := httpclient.Delete(url, nil, deleteHeader)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logger.Error().Msgf("Failed to delete RXSWINs from SUMS:\n Status: %s\n Version: %s\n", resp.Status, version)
	}

	return resp, nil
}
