package lob

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/rs/zerolog/log"
	"resty.dev/v3"
)

type Lob struct {
	APIKey string

	client     *resty.Client
	urlBaseApi string
}

func NewLob(api_key string) *Lob {
	r := resty.New()
	if os.Getenv("LOB_INSECURE_SKIP_VERIFY") != "" {
		log.Warn().Msg("Using insecure TLS verification settings")
		r.SetTLSClientConfig(&tls.Config{
			InsecureSkipVerify: true,
		})
	}
	r.SetBasicAuth(api_key, "")
	l := &Lob{
		APIKey:     api_key,
		client:     r,
		urlBaseApi: "api.lob.com",
	}
	return l
}

type Address struct {
	ID             string                 `json:"id"`
	Description    string                 `json:"description"`
	Name           string                 `json:"name"`
	Company        string                 `json:"company"`
	Phone          *string                `json:"phone"`
	Email          *string                `json:"email"`
	AddressLine1   string                 `json:"address_line1"`
	AddressLine2   *string                `json:"address_line2"`
	AddressCity    string                 `json:"address_city"`
	AddressState   string                 `json:"address_state"`
	AddressZip     string                 `json:"address_zip"`
	AddressCountry string                 `json:"address_country"`
	Metadata       map[string]interface{} `json:"metadata"`
	DateCreated    string                 `json:"date_created"`
	DateModified   string                 `json:"date_modified"`
	RecipientMoved bool                   `json:"recipient_moved"`
	Object         string                 `json:"object"`
}
type Letter struct {
	ID                   string                 `json:"id"`
	Description          string                 `json:"description"`
	Metadata             map[string]interface{} `json:"metadata"`
	To                   Address                `json:"to"`
	From                 Address                `json:"from"`
	Color                bool                   `json:"color"`
	DoubleSided          bool                   `json:"double_sided"`
	AddressPlacement     string                 `json:"address_placement"`
	ReturnEnvelope       bool                   `json:"return_envelope"`
	PerforatedPage       *int                   `json:"perforated_page"`
	ExtraService         string                 `json:"extra_service"`
	CustomEnvelope       *string                `json:"custom_envelope"`
	TemplateID           string                 `json:"template_id"`
	TemplateVersionID    string                 `json:"template_version_id"`
	MailType             string                 `json:"mail_type"`
	URL                  string                 `json:"url"`
	MergeVariables       map[string]interface{} `json:"merge_variables"`
	Carrier              string                 `json:"carrier"`
	TrackingNumber       string                 `json:"tracking_number"`
	TrackingEvents       []interface{}          `json:"tracking_events"`
	Thumbnails           []interface{}          `json:"thumbnails"`
	ExpectedDeliveryDate string                 `json:"expected_delivery_date"`
	DateCreated          string                 `json:"date_created"`
	DateModified         string                 `json:"date_modified"`
	SendDate             string                 `json:"send_date"`
	UseType              string                 `json:"use_type"`
	FSC                  bool                   `json:"fsc"`
	Object               string                 `json:"object"`
}
type ResponseAddressList struct {
	Addresses  []Address `json:"data"`
	Count      int       `json:"count"`
	CountTotal int       `json:"total_count"`
}
type ResponseLetterList struct {
	Letters    []Letter `json:"data"`
	Count      int      `json:"count"`
	CountTotal int      `json:"total_count"`
}

type RequestAddressCreate struct {
	AddressLine1 string `json:"address_line1"`
	AddressCity  string `json:"address_city"`
	AddressState string `json:"address_state"`
	AddressZip   string `json:"address_zip"`
	Name         string `json:"name"`
}
type RequestLetterCreate struct {
	Color   bool
	From    string
	File    io.Reader
	To      string
	UseType string
}

/*
	{
	    "error": {
	        "message": "address_zip is required",
	        "status_code": 422,
	        "code": "invalid"
	    }
	}
*/
type Error struct {
	Code       string `json:"code"`
	Message    string `json:"message"`
	StatusCode int    `json:"status_code"`
}
type ResponseError struct {
	InnerError Error `json:"error"`
}

func (re ResponseError) Error() string {
	return fmt.Sprintf("%d %s %s", re.InnerError.StatusCode, re.InnerError.Code, re.InnerError.Message)
}

func (l *Lob) AddressCreate(ctx context.Context, req RequestAddressCreate) (Address, error) {
	ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	var result Address
	var error_response ResponseError
	resp, err := l.client.R().
		SetBody(req).
		SetContext(ctx).
		SetContentType("application/json").
		SetError(&error_response).
		SetResult(&result).
		SetPathParam("urlBase", l.urlBaseApi).
		Post("https://{urlBase}/v1/addresses")
	if err != nil {
		return result, fmt.Errorf("address list post: %w", err)
	}
	if !resp.IsSuccess() {
		return result, fmt.Errorf("address create not successful: %w", error_response)
	}
	return result, nil
}
func (l *Lob) AddressList(ctx context.Context) ([]Address, error) {
	ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	var result ResponseAddressList
	var error_response ResponseError

	resp, err := l.client.R().
		//SetQueryParamsFromValues(query).
		SetContext(ctx).
		SetError(&error_response).
		SetResult(&result).
		SetPathParam("urlBase", l.urlBaseApi).
		Get("https://{urlBase}/v1/addresses")
	if err != nil {
		return nil, fmt.Errorf("address list get: %w", err)
	}
	if !resp.IsSuccess() {
		return nil, fmt.Errorf("address list not successful: %w", error_response)
	}
	return result.Addresses, nil
}

func (l *Lob) LetterCreate(ctx context.Context, req RequestLetterCreate) (Letter, error) {
	ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	var error_response ResponseError
	var result Letter
	color_str := "false"
	if req.Color {
		color_str = "true"
	}
	resp, err := l.client.R().
		SetContext(ctx).
		SetError(&error_response).
		SetMultipartField(
			"file",
			"content.pdf",
			"application/pdf",
			req.File,
		).
		SetMultipartFormData(map[string]string{
			"color":    color_str,
			"from":     req.From,
			"to":       req.To,
			"use_type": req.UseType,
		}).
		SetResult(&result).
		SetPathParam("urlBase", l.urlBaseApi).
		Post("https://{urlBase}/v1/letters")
	if err != nil {
		return result, fmt.Errorf("letters list post: %w", err)
	}
	if !resp.IsSuccess() {
		return result, fmt.Errorf("letter create not successful. %w", error_response)
	}
	return result, nil
}
func (l *Lob) LetterList(ctx context.Context) ([]Letter, error) {
	ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	var error_response ResponseError
	var result ResponseLetterList

	resp, err := l.client.R().
		//SetQueryParamsFromValues(query).
		SetContext(ctx).
		SetError(&error_response).
		SetResult(&result).
		SetPathParam("urlBase", l.urlBaseApi).
		Get("https://{urlBase}/v1/letters")
	if err != nil {
		return nil, fmt.Errorf("letter list get: %w", err)
	}
	if !resp.IsSuccess() {
		return nil, fmt.Errorf("letter list not successful. Error: %w", error_response)
	}
	return result.Letters, nil
}
