package apshared

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"

	webshared "git.mstar.dev/mstar/linstrom/web/shared"
)

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

type LinkData struct {
	// What type of link this is
	// - `self` refers to the activitypub object and has Type and Href set
	// - `http://webfinger.net/rel/profile-page` refers to the public webpage of the account and has Type and Href set
	// - `http://ostatus.org/schema/1.0/subscribe` provides a template for subscribing/following the account. Has Template set
	//   Template will contain a `{uri}` part with which to replace idk yet
	Relation string `json:"rel"`
	// The content type of the url
	Type *string `json:"type"`
	// The url
	Href *string `json:"href"`
	// Template to use for something
	Template *string `json:"template"`
}

// Data returned from a webfinger response (and also sent when asked for an account via webfinger)
type WebfingerData struct {
	// What this webfinger data refers to. Accounts are usually `acct:username@host`
	Subject string `json:"subject"`
	// List of links related to the account
	Links []LinkData `json:"links"`
}

var ErrAccountNotFound = errors.New("account not found")
var ErrRemoteServerFailed = errors.New("remote server errored out")

// Get the webfinger data for a given full account handle (like @bob@example.com or bob@example.com)
func GetAccountWebfinger(fullHandle string) (*WebfingerData, error) {
	// First get the handle and server domain from the full handle (and ensure it's a correctly formatted one)
	handle, server, err := SplitFullHandle(fullHandle)
	if err != nil {
		return nil, err
	}
	webfingerRequest, err := http.NewRequest(
		// Webfinger requests are GET
		"GET",
		// The webfinger url is located at <domain>/.well-known/webfinger
		// The url parameter `resource` tells the remote server what data we want, should always be an account
		// The prefix `acct:<full-handle>` (where full-handle is in the form of bob@example.com)
		// tells the remote server that we want the account data of the given account handle
		"https://"+server+"/.well-known/webfinger?resource=acct:"+handle+"@"+server,
		// No request body since it's a get request and we only need the url parameter
		nil,
	)
	if err != nil {
		return nil, err
	}
	// Then send the request
	result, err := webshared.RequestClient.Do(webfingerRequest)
	if err != nil {
		return nil, err
	}

	// Code 404 indicates that the account doesn't exist
	if result.StatusCode == 404 {
		return nil, ErrAccountNotFound
	} else if result.StatusCode != 200 {
		// And anything other than 200 or 404 is an error for now
		// TODO: Add information gathering here to figure out what the remote server's problem is
		return nil, ErrRemoteServerFailed
	}

	// Get the body data from the response
	data, err := io.ReadAll(result.Body)
	if err != nil {
		return nil, err
	}

	// Then try and parse it into webfinger data
	webfinger := WebfingerData{}
	err = json.Unmarshal(data, &webfinger)
	if err != nil {
		return nil, err
	}

	return &webfinger, nil
}
