package ap

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"

	"gitlab.com/mstarongitlab/goap"
)

var ErrNoApUrl = errors.New("no Activitypub url in webfinger")

func GetRemoteUser(fullHandle string) (goap.BaseApChain, error) {
	webfinger, err := GetAccountWebfinger(fullHandle)
	if err != nil {
		return nil, err
	}

	apUrl := ""
	for _, link := range webfinger.Links {
		if link.Relation == "self" {
			apUrl = *link.Href
		}
	}
	if apUrl == "" {
		return nil, ErrNoApUrl
	}
	apRequest, err := http.NewRequest("GET", apUrl, nil)
	if err != nil {
		return nil, err
	}
	apRequest.Header.Add("Accept", "application/activity+json,application/ld+json,application/json")
	client := http.Client{Timeout: time.Second * 30}
	res, err := client.Do(apRequest)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("bad status code: %d", res.StatusCode)
	}
	body, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	apObject, _ := goap.Unmarshal(body, nil, nil)
	// Check if Id exists
	if _, ok := goap.FindAttribute[*goap.UDIdData](apObject); !ok {
		return nil, fmt.Errorf("missing attribute for account: Id")
	}
	// Check that it has the correct object type for an account
	if objTypePtr, ok := goap.FindAttribute[*goap.UDTypeData](apObject); !ok {
		return nil, fmt.Errorf("missing attribute for account: Type")
	} else if objType := *objTypePtr; objType.Type != goap.KEY_ACTIVITYSTREAMS_ACTOR {
		return nil, fmt.Errorf("wrong ap object type: %s", objType.Type)
	}
	// And finally check for inbox
	if _, ok := goap.FindAttribute[*goap.W3InboxData](apObject); !ok {
		return nil, fmt.Errorf("missing attribute for account: Inbox")
	}
	return apObject, nil
}

func GetRemoteObject(target string) (goap.BaseApChain, error) {
	apRequest, err := http.NewRequest("GET", target, nil)
	if err != nil {
		return nil, err
	}
	apRequest.Header.Add("Accept", "application/activity+json,application/ld+json,application/json")
	client := http.Client{Timeout: time.Second * 30}
	res, err := client.Do(apRequest)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("bad status code: %d", res.StatusCode)
	}
	body, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	apObject, _ := goap.Unmarshal(body, nil, nil)
	return apObject, nil
}
