package cloudflare

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

type CloudflareApi struct {
	client http.Client
}

func NewCloudflareApi(opts ...CloudflareApiOption) *CloudflareApi {
	api := &CloudflareApi{http.Client{Timeout: 30 * time.Second}}
	for _, opt := range opts {
		opt(api)
	}
	return api
}

func (api *CloudflareApi) CreateProfile(ctx context.Context, publicKey string) (*CloudflareProfile, error) {
	serial, err := GenerateRandomAndroidSerial()
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial: %v", err)
	}
	data := Registration{
		Key:       publicKey,
		InstallID: "",
		FcmToken:  "",
		Tos:       TimeAsCfString(time.Now()),
		Model:     "PC",
		Serial:    serial,
		OsVersion: "",
		KeyType:   KeyTypeWg,
		TunType:   TunTypeWg,
		Locale:    "en-US",
	}
	jsonData, err := json.Marshal(data)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal json: %v", err)
	}
	request, err := http.NewRequest("POST", ApiUrl+"/"+ApiVersion+"/reg", bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, err
	}
	for k, v := range Headers {
		request.Header.Set(k, v)
	}
	response, err := api.client.Do(request.WithContext(ctx))
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to register: %v", response.StatusCode)
	}
	profile := new(CloudflareProfile)
	return profile, json.NewDecoder(response.Body).Decode(profile)
}

func (api *CloudflareApi) EnrollKey(ctx context.Context, authToken string, id string, keyType, tunType, publicKey string) (*CloudflareProfile, error) {
	deviceUpdate := DeviceUpdate{
		Name:    "PC",
		Key:     publicKey,
		KeyType: keyType,
		TunType: tunType,
	}
	jsonData, err := json.Marshal(deviceUpdate)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal json: %v", err)
	}
	request, err := http.NewRequest("PATCH", ApiUrl+"/"+ApiVersion+"/reg/"+id, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, err
	}
	for k, v := range Headers {
		request.Header.Set(k, v)
	}
	request.Header.Set("Authorization", "Bearer "+authToken)
	response, err := api.client.Do(request.WithContext(ctx))
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to enroll key: %v", response.StatusCode)
	}
	profile := new(CloudflareProfile)
	return profile, json.NewDecoder(response.Body).Decode(profile)
}

func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
	request, err := http.NewRequest("GET", ApiUrl+"/"+ApiVersion+"/reg/"+id, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range Headers {
		request.Header.Set(k, v)
	}
	request.Header.Set("Authorization", "Bearer "+authToken)
	response, err := api.client.Do(request.WithContext(ctx))
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get profile: %v", response.StatusCode)
	}
	profile := new(CloudflareProfile)
	return profile, json.NewDecoder(response.Body).Decode(profile)
}
