package coach_client

import (
	"context"
	"fmt"
	"log/slog"
	"net/url"
	"os"
	"time"

	"gitea.timerzz.com/kedaya_haitao/common/pkg/proxy"
	restry_pool "gitea.timerzz.com/kedaya_haitao/common/pkg/restry-pool"
	healthcheck "gitea.timerzz.com/timerzz/proxy-detector/pkg/health-check"
	"gitea.timerzz.com/timerzz/proxy-detector/pkg/proxy/structs"
	"github.com/go-resty/resty/v2"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

const (
	BaseUrl_Coach       = "https://www.coach.com"
	BaseUrl_CoachOutlet = "https://www.coachoutlet.com"
)

const (
	USClient_Type_Coach        = "coach"
	USClient_Type_Coach_Outlet = "coach_outlet"
)

const (
	USClient_Type_Env_Key = "COACH_CLIENT_TYPE"
)

func LoadUSClientType() string {
	clientType := os.Getenv(USClient_Type_Env_Key)
	if clientType == "" {
		glog.Fatal("加载coach客户端类型失败")
	}
	if clientType != USClient_Type_Coach && clientType != USClient_Type_Coach_Outlet {
		glog.Fatal("加载coach客户端类型不正确")
	}
	return clientType
}

type USClientBuilder struct {
	pool *proxy.Pool
}

func US(pool *proxy.Pool) *USClientBuilder {
	return &USClientBuilder{
		pool: pool,
	}
}

func (u *USClientBuilder) Coach() USClient {
	return &us{
		pool:           u.pool,
		defaultTimeOut: 10 * time.Minute,
		baseUrl:        BaseUrl_Coach,
	}
}

func (u *USClientBuilder) CoachOutlet() USClient {
	return &us{
		pool:           u.pool,
		defaultTimeOut: 10 * time.Minute,
		baseUrl:        BaseUrl_CoachOutlet,
	}
}

func (u *USClientBuilder) CreateByType(clientType string) USClient {
	if clientType == USClient_Type_Coach {
		return u.Coach()
	} else if clientType == USClient_Type_Coach_Outlet {
		return u.CoachOutlet()
	}
	return nil
}

type USClient interface {
	RequestInventory(ctx context.Context, pid string) (inv Inventory, err error)
	RequestProductDetail(ctx context.Context, pid string) (data ProductData, err error)
	ViewAllBags(ctx context.Context, page int) (resp PageDataResponse, err error)
	BaseUrl() string
}

type us struct {
	pool           *proxy.Pool
	defaultTimeOut time.Duration
	baseUrl        string
}

func tryRequest(ctx context.Context, base, urlPath string, respData any, proxyGetter func() *structs.Proxy) error {
	for p := proxyGetter(); p != nil; p = proxyGetter() {
		select {
		case <-ctx.Done():
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				return fmt.Errorf("超时未获取到结果,链接:%s", urlPath)
			}
			return nil
		default:

		}
		resp, err := callByProxy(ctx, p, base, urlPath, respData)
		if err != nil {
			slog.Debug(err.Error())
			continue
		}
		if resp.StatusCode() != 200 {
			slog.Debug(fmt.Errorf("请求错误:%d, %s", resp.StatusCode(), resp.Body()).Error())
			continue
		}
		return nil
	}
	return errors.New("未获取到可用的代理")
}

func callByProxy(ctx context.Context, p *structs.Proxy, base, urlPath string, result any) (*resty.Response, error) {
	full, _ := url.JoinPath(base, urlPath)
	addr, err := healthcheck.UrlToMetadata(full)
	if err != nil {
		return nil, err
	}
	subCtx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	conn, err := p.ClashProxy().DialContext(subCtx, &addr)
	if err != nil {
		return nil, errors.Wrap(err, "创建conn失败")
	}
	defer conn.Close()

	var cli = restry_pool.GetRestyClient(conn)
	defer restry_pool.PutRestyClient(cli)
	return cli.SetBaseURL(base).R().SetResult(result).Get(urlPath)
}

type Inventory struct {
	Id                  string    `json:"id"`
	Ats                 int       `json:"ats"`
	PreOrderable        bool      `json:"preorderable"`
	BackOrderable       bool      `json:"backorderable"`
	Orderable           bool      `json:"orderable"`
	AllocationResetDate time.Time `json:"allocationResetDate,omitempty"`
	Perpetual           bool      `json:"perpetual"`
	StockLevel          int       `json:"stockLevel"`
}

type InventoryResponse struct {
	Inventory struct {
		Status          string    `json:"status"`
		InventoryListID string    `json:"inventoryListID"`
		InventoryInfo   Inventory `json:"inventoryInfo"`
	} `json:"inventory"`
}

func (c *us) BaseUrl() string {
	return c.baseUrl
}

func (c *us) RequestInventory(ctx context.Context, pid string) (inv Inventory, err error) {
	sctx, cancel := context.WithTimeout(ctx, c.defaultTimeOut)
	defer cancel()
	var resp InventoryResponse
	urlPath := fmt.Sprintf("/api/inventory?vgId=%s&includeVariantData=false", url.QueryEscape(pid))
	err = tryRequest(sctx, c.baseUrl, urlPath, &resp, c.pool.RandomIterator())
	inv = resp.Inventory.InventoryInfo
	return
}

type ProductData struct {
	Id          string       `json:"id"`
	Name        string       `json:"name"`
	Brand       string       `json:"brand"`
	Inventory   Inventory    `json:"inventory"`
	Url         string       `json:"url"`
	MasterId    string       `json:"masterId"`
	ImageGroups []ImageGroup `json:"imageGroups"`
	Prices      struct {
		CurrentPrice float64 `json:"currentPrice"`
	} `json:"prices"`
	Remark string `json:"-"`
}
type ImageGroup struct {
	Images []struct {
		Src   string `json:"src"`
		Title string `json:"title"`
		Alt   string `json:"alt"`
	} `json:"images"`
	ViewType string `json:"viewType"`
}
type ProductDataResponse struct {
	ProductData []*ProductData `json:"productsData"`
}

func (c *us) RequestProductDetail(ctx context.Context, pid string) (data ProductData, err error) {
	sctx, cancel := context.WithTimeout(ctx, c.defaultTimeOut)
	defer cancel()
	var resp ProductDataResponse
	urlPath := fmt.Sprintf("/api/get-products?ids=%s&includeInventory=false", url.QueryEscape(pid))
	err = tryRequest(sctx, c.baseUrl, urlPath, &resp, c.pool.RandomIterator())
	if len(resp.ProductData) == 0 && err != nil {
		err = fmt.Errorf("获取详情信息为空")
		return
	}
	if len(resp.ProductData) > 0 {
		data = *resp.ProductData[0]
	}
	return
}

type PageDataResponse struct {
	PageData struct {
		Total      int       `json:"total"`
		Page       int       `json:"page"`
		TotalPages int       `json:"totalPages"`
		PageSize   int       `json:"pageSize"`
		Products   []Product `json:"products"`
	} `json:"pageData"`
}

type Product struct {
	Name           string    `json:"name"`
	Colors         []Color   `json:"colors"`
	VariantsOnSale []Variant `json:"variantsOnSale"`
}

type Color struct {
	Id        string `json:"id"`   //"id": "IMDQC",
	Text      string `json:"text"` //颜色的描述
	Orderable bool   `json:"orderable"`
	Media     Media  `json:"media"`
	Url       string `json:"url"`      //"/products/eliza-flap-crossbody-bag-in-signature-canvas/CP009-IMDQC.html",
	VgId      string `json:"vgId"`     // "vgId": "CP009-IMDQC",
	MasterId  string `json:"masterId"` //"masterId": "CP009"
}

type Media struct {
	Thumbnail Thumbnail `json:"thumbnail"`
}

type Thumbnail struct {
	Src string `json:"src"`
}

type Variant struct {
	Id     string `json:"id"`
	OnSale bool   `json:"onSale"`
	Price  Price  `json:"price"`
}

type Price struct {
	Sales               Sales `json:"sales"`
	MarkdownDiscPercent int   `json:"markdownDiscPercent"`
}

type Sales struct {
	Value        float64 `json:"value"`
	Currency     string  `json:"currency"`
	Formatted    string  `json:"formatted"`
	DecimalPrice string  `json:"decimalPrice"`
}

func (c *us) ViewAllBags(ctx context.Context, page int) (resp PageDataResponse, err error) {
	if page < 1 {
		page = 1
	}
	sctx, cancel := context.WithTimeout(ctx, c.defaultTimeOut)
	defer cancel()
	urlPath := fmt.Sprintf("/api/get-shop/bags/view-all?page=%d", page)
	err = tryRequest(sctx, c.baseUrl, urlPath, &resp, c.pool.RandomIterator())
	return
}