package browser

import (
	"context"
	"time"

	"github.com/chromedp/cdproto/network"
	"github.com/chromedp/chromedp"
)

type Cookie struct {
	Name   string
	Value  string
	Domain string
}

type cookieContext struct{}

func NewCookieContext(ctx context.Context, cookie []Cookie) context.Context {
	return context.WithValue(ctx, cookieContext{}, cookie)
}

func CookieFromContext(ctx context.Context) []Cookie {
	return ctx.Value(cookieContext{}).([]Cookie)
}

type LocalStorage struct {
	Key   string
	Value string
}

type localStorageContext struct{}

func NewLocalStorageContext(ctx context.Context, localStorage []LocalStorage) context.Context {
	return context.WithValue(ctx, localStorageContext{}, localStorage)
}

func LocalStorageFromContext(ctx context.Context) []LocalStorage {
	if val := ctx.Value(localStorageContext{}); val != nil {
		return val.([]LocalStorage)
	}
	return []LocalStorage{}
}

type Allocator struct {
	context.Context
	ActTimeout time.Duration
}

func NewHeadlessAllocator(
	ctx context.Context,
) (Allocator, context.CancelFunc) {
	ctx, cancel := chromedp.NewExecAllocator(ctx,
		append(chromedp.DefaultExecAllocatorOptions[:],
			chromedp.Headless,
			// chromedp.Flag("headless", false),
			chromedp.DisableGPU,
			chromedp.NoSandbox,
		)...,
	)

	mainCtx, mainCancel := chromedp.NewContext(ctx)

	// Listen and ignore network events that might cause IPAddressSpace unmarshaling errors
	chromedp.ListenTarget(mainCtx, func(ev interface{}) {
		switch ev.(type) {
		case *network.EventRequestWillBeSent,
			*network.EventResponseReceived,
			*network.EventLoadingFinished,
			*network.EventLoadingFailed,
			*network.EventDataReceived:
			// Ignore network events to prevent IPAddressSpace errors
			return
		}
	})

	cancelFunc := func() {
		mainCancel()
		cancel()
	}

	return Allocator{
		Context:    mainCtx,
		ActTimeout: 30 * time.Second,
	}, cancelFunc
}

func (a Allocator) NewTimeOutContext(timeout time.Duration) (context.Context, context.CancelFunc) {
	if timeout == 0 {
		timeout = a.ActTimeout
	}

	return context.WithTimeout(a.Context, timeout)
}
