package tmobile

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	errorsO "errors"

	"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
	tmtg "github.com/fiskerinc/cloud-services/pkg/tmobtokengen"
	"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
	"github.com/pkg/errors"
)

type TMobileMiniClient struct {
	client      httpClienter
	tg          tmtg.Generator
	accessToken string
	baseURL     string
}



// Meant to be used within 30 minutes, and then disposed of
func NewTMobileMiniClient() (client *TMobileMiniClient, err error) {
	client = &TMobileMiniClient{}
	tg, err := InitTokenGen(
		envtool.GetEnv("TMOBILE_PRIVATE_KEY", ""),
		envtool.GetEnv("TMOBILE_CLIENT_ID", ""),
		envtool.GetEnv("TMOBILE_SECRET", ""),
	)
	if err != nil {
		return
	}

	client.tg = tg
	client.baseURL = Endpoint
	client.client = &http.Client{
		Timeout: time.Minute,
	}

	acsToken, err := client.AccessToken(context.Background())
	if err != nil {
		return
	}
	client.SetAccessToken(acsToken.AccessToken)
	return
}

// AccessToken implements TMobClienter.
func (tmc *TMobileMiniClient) AccessToken(ctx context.Context) (out *AccessTokenResponse, err error) {
	path := "/oauth2/v1/tokens"
	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.tg.ClientSecretAsAuthVal()).
		SetURI(path).
		SetHTTPMethod(http.MethodPost)

	token, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, err
	}

	fullPath := tmc.baseURL + path

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, nil)
	if err != nil {
		return nil, errors.WithMessagef(ErrCreateRequest, "failed to create request")
	}

	req.Header.Set(Authorization, tmc.tg.ClientSecretAsAuthVal())
	req.Header.Set(XAuthorization, token)

	err = tmc.do(req, &out)
	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}
	return
}


// HandleChangeDeviceStatus implements TMobClienter.
func (tmc *TMobileMiniClient) ChangeDeviceStatus(ctx context.Context, cda ChangeDeviceActivation) (err error) {
	var fn func(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error)
	if cda.Enabled {
		fn = tmc.handleServiceRestore
	}else {
		fn = tmc.handleServiceCancel
	}
	for _, iccid := range cda.ICCIDs {
		iccid = strings.TrimSuffix(strings.TrimSuffix(iccid, "F"), "f")
		temp := ICCIDBody{
			ICCID: iccid,
		}
		_, tErr := fn(ctx, &temp)
		if tErr != nil {
			err = errorsO.Join(err, tErr)
		}
	}
	return err
}

// ChangeRatePlan implements TMobClienter.
func (tmc *TMobileMiniClient) ChangeRatePlan(context.Context, *ChangeRatePlanRequest) (*ChangeRatePlanResponse, error) {
	panic("unimplemented")
}

// CustomAttributes implements TMobClienter.
func (tmc *TMobileMiniClient) CustomAttributes(ctx context.Context, in *CustomAtributesRequest) (*CustomAtributesResponse, error) {
	path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/device-attributes"
	fullPath := tmc.baseURL + path

	payload := new(bytes.Buffer)
	if err := json.NewEncoder(payload).Encode(in); err != nil {
		return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err)
	}
	payloadStr := payload.String()

	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken).
		SetURI(path).
		SetHTTPMethod(http.MethodPost).
		SetContentType(applicationJSON).
		SetBody(payloadStr)

	popToken, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload)
	if err != nil {
		return nil, err
	}

	req.Header.Set(Authorization, tmc.accessToken)
	req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken))
	req.Header.Set(XAuthorization, popToken)
	req.Header.Set(contentType, applicationJSON)
	out := &CustomAtributesResponse{}

	err = tmc.do(req, out)
	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}

	return out, nil
}

// Details implements TMobClienter.
func (tmc *TMobileMiniClient) Details(ctx context.Context, iccid string) (out *SMSDetailsResponse, err error) {
	// Really not the way
	path := fmt.Sprintf("/eitcsr-iotcp-notifications-v2/prd02/iotcp/v2/notifications/sms/messages/%s", iccid)

	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken).
		SetURI(path).
		SetHTTPMethod(http.MethodGet)

	token, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, errors.WithMessagef(ErrTokenGen, "failed to generate token: %v", err)
	}

	fullPath := tmc.baseURL + path

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullPath, nil)
	if err != nil {
		return nil, errors.WithMessagef(ErrCreateRequest, "failed to create request: %v", err)
	}

	req.Header.Set(Authorization, tmc.accessToken)
	req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken))
	req.Header.Set(XAuthorization, token)
	req.Header.Set(contentType, applicationJSON)

	err = tmc.do(req, &out)
	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}

	return
}

// DeviceDetails implements TMobClienter.
func (tmc *TMobileMiniClient) DeviceDetails(ctx context.Context, in *DeviceDetailsRequest) (out *DeviceDetailsResponse, err error) {
	path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/details"
	fullPath := tmc.baseURL + path

	payload := new(bytes.Buffer)
	if err := json.NewEncoder(payload).Encode(in); err != nil {
		return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err)
	}
	payloadStr := payload.String()

	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken).
		SetURI(path).
		SetHTTPMethod(http.MethodPost).
		SetContentType(applicationJSON).
		SetBody(payloadStr)

	popToken, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload)
	if err != nil {
		return nil, err
	}

	req.Header.Set(Authorization, tmc.accessToken)
	req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken))
	req.Header.Set(XAuthorization, popToken)
	req.Header.Set(contentType, applicationJSON)
	out = &DeviceDetailsResponse{}

	err = tmc.do(req, &out)
	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}

	return out, nil
}

// GetProducts implements TMobClienter.
func (tmc *TMobileMiniClient) GetProducts(context.Context, *sms.GetAvailableProductsRequest) (*sms.GetAvailableProductsResponse, error) {
	panic("unimplemented")
}

// SendSMS implements TMobClienter.
func (tmc *TMobileMiniClient) SendSMS(ctx context.Context, in *SendSMSRequest) (out *SendSMSResponse, err error) {
	panic("unimplemented")
}

// SetAccessToken implements TMobClienter.
// Possible one dumb piece of code
func (tmc *TMobileMiniClient) SetAccessToken(accessToken string) {
	tmc.accessToken = "Bearer " + accessToken
}

// SetFilter implements TMobClienter.
func (tmc *TMobileMiniClient) SetFilter(filter []string) {
	panic("unimplemented")
}

var _ TMobClienter = &TMobileMiniClient{}

func (c *TMobileMiniClient) do(req *http.Request, out interface{}) error {

	resp, err := c.client.Do(req)
	if err != nil {
		return errors.WithMessagef(ErrDoRequest, "failed to do request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		var body []byte
		body, err = io.ReadAll(resp.Body)
		if err != nil {
			return errors.WithMessagef(ErrReadResponseBody, "failed to read response body: %v", err)
		}

		toWrapErr := ErrBadStatusCode
		if resp.StatusCode == http.StatusUnauthorized {
			toWrapErr = ErrAccessTokenExpired
		} else if resp.StatusCode == http.StatusBadGateway {
			toWrapErr = ErrBadGatewayCode
		} else if resp.StatusCode == http.StatusGatewayTimeout {
			toWrapErr = ErrBadGatewayCode
		}

		return errors.WithMessagef(toWrapErr, "request failed with status code %d, body: %s", resp.StatusCode, string(body))
	}

	err = json.NewDecoder(resp.Body).Decode(out)
	if err != nil {
		return errors.WithMessagef(ErrJSONMarshal, "failed to decode response: %v", err)
	}

	return nil
}


// sets status as ACTIVATED
func (tmc *TMobileMiniClient) handleServiceRestore(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error){
	path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/service-restore"
	fullPath := tmc.baseURL + path

	payload := new(bytes.Buffer)
	err = json.NewEncoder(payload).Encode(in)
	if err != nil {
		return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err)
	}
	payloadStr := payload.String()
	fmt.Println(payloadStr)

	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken).
		SetURI(path).
		SetHTTPMethod(http.MethodPost).
		SetContentType(applicationJSON).
		SetBody(payloadStr)
	popToken, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload)
	if err != nil {
		return nil, err
	}

	req.Header.Set(Authorization, tmc.accessToken)
	req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken))
	req.Header.Set(XAuthorization, popToken)
	req.Header.Set(contentType, applicationJSON)
	out = &ICCIDBody{}

	err = tmc.do(req, &out)

	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}
	return
}

// sets status as DEACTIVATED
func (tmc *TMobileMiniClient) handleServiceCancel(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error){
	path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/service-suspend"
	fullPath := tmc.baseURL + path

	payload := new(bytes.Buffer)
	err = json.NewEncoder(payload).Encode(in)
	if err != nil {
		return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err)
	}
	payloadStr := payload.String()

	emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken).
		SetURI(path).
		SetHTTPMethod(http.MethodPost).
		SetContentType(applicationJSON).
		SetBody(payloadStr)
	popToken, err := tmc.tg.Generate(emap)
	if err != nil {
		return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload)
	if err != nil {
		return nil, err
	}

	req.Header.Set(Authorization, tmc.accessToken)
	req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken))
	req.Header.Set(XAuthorization, popToken)
	req.Header.Set(contentType, applicationJSON)
	out = &ICCIDBody{}
	
	err = tmc.do(req, &out)

	if err != nil {
		return nil, errors.WithMessage(err, failedToDoRequest)
	}
	return
}