// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package whatsmeow

import (
	"context"
	"fmt"
	"time"

	waBinary "go.mau.fi/whatsmeow/binary"
	"go.mau.fi/whatsmeow/types"
)

//type MediaConnIP struct {
//	IP4 net.IP
//	IP6 net.IP
//}

// MediaConnHost represents a single host to download media from.
type MediaConnHost struct {
	Hostname string
	//IPs      []MediaConnIP
}

// MediaConn contains a list of WhatsApp servers from which attachments can be downloaded from.
type MediaConn struct {
	Auth       string
	AuthTTL    int
	TTL        int
	MaxBuckets int
	FetchedAt  time.Time
	Hosts      []MediaConnHost
}

// Expiry returns the time when the MediaConn expires.
func (mc *MediaConn) Expiry() time.Time {
	return mc.FetchedAt.Add(time.Duration(mc.TTL) * time.Second)
}

func (cli *Client) refreshMediaConn(ctx context.Context, force bool) (*MediaConn, error) {
	if cli == nil {
		return nil, ErrClientIsNil
	}
	cli.mediaConnLock.Lock()
	defer cli.mediaConnLock.Unlock()
	if cli.mediaConnCache == nil || force || time.Now().After(cli.mediaConnCache.Expiry()) {
		var err error
		cli.mediaConnCache, err = cli.queryMediaConn(ctx)
		if err != nil {
			return nil, err
		}
	}
	return cli.mediaConnCache, nil
}

func (cli *Client) queryMediaConn(ctx context.Context) (*MediaConn, error) {
	resp, err := cli.sendIQ(infoQuery{
		Context:   ctx,
		Namespace: "w:m",
		Type:      "set",
		To:        types.ServerJID,
		Content:   []waBinary.Node{{Tag: "media_conn"}},
	})
	if err != nil {
		return nil, fmt.Errorf("failed to query media connections: %w", err)
	} else if len(resp.GetChildren()) == 0 || resp.GetChildren()[0].Tag != "media_conn" {
		return nil, fmt.Errorf("failed to query media connections: unexpected child tag")
	}
	respMC := resp.GetChildren()[0]
	var mc MediaConn
	ag := respMC.AttrGetter()
	mc.FetchedAt = time.Now()
	mc.Auth = ag.String("auth")
	mc.TTL = ag.Int("ttl")
	mc.AuthTTL = ag.Int("auth_ttl")
	mc.MaxBuckets = ag.Int("max_buckets")
	if !ag.OK() {
		return nil, fmt.Errorf("failed to parse media connections: %+v", ag.Errors)
	}
	for _, child := range respMC.GetChildren() {
		if child.Tag != "host" {
			cli.Log.Warnf("Unexpected child in media_conn element: %s", child.XMLString())
			continue
		}
		cag := child.AttrGetter()
		mc.Hosts = append(mc.Hosts, MediaConnHost{
			Hostname: cag.String("hostname"),
		})
		if !cag.OK() {
			return nil, fmt.Errorf("failed to parse media connection host: %+v", ag.Errors)
		}
	}
	return &mc, nil
}
