package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

// CreateObject - Used by post web receiver
type CreateObject struct {
	ID      string   `json:"id"`
	Actor   string   `json:"actor"`
	Cc      []string `json:"cc"`
	Content string   `json:"content"`
	To      []string `json:"to"`
	Type    string   `json:"type"`
}

// CreateObject - Used by post web receiver
type AnnounceObject struct {
	ID     string   `json:"id"`
	Actor  string   `json:"actor"`
	To     []string `json:"to"`
	Type   string   `json:"type"`
	Object string   `json:"object"`
}

// RelayBase - The base object used by web receiver
type RelayBase struct {
	Actor     string          `json:"actor"`
	Cc        []string        `json:"cc"`
	Object    json.RawMessage `json:"Object"`
	ID        string          `json:"id"`
	Published string          `json:"published"`
	To        []string        `json:"to"`
	Type      string          `json:"type"`
}

func hostmeta(w http.ResponseWriter, r *http.Request) {
	fmt.Println("PATH --> ", r.URL.Path)
	host := r.Host
	xml := "<?xml version=\"1.0\" encoding=\"UTF-8\"?><XRD xmlns=\"http://docs.oasis-open.org/ns/xri/xrd-1.0\"><Link rel=\"lrdd\" template=\"https://" + host + "/.well-known/webfinger?resource={uri}\" type=\"application/xrd+xml\" /></XRD>"
	w.Header().Set("Content-Type", "application/xrd+xml")
	fmt.Fprintf(w, xml)
}

func webfinger(w http.ResponseWriter, r *http.Request) {
	fmt.Println("PATH --> ", r.URL.Path)
	host := r.Host

	webfingermap := make(map[string]interface{})
	webfingermap["subject"] = "acct:fedilogue@" + host
	webfingermap["aliases"] = []string{"https://" + host + "/users/fedilogue"}
	link0 := make(map[string]string)
	link0["rel"] = "http://webfinger.net/rel/profile-page"
	link0["type"] = "text/html"
	link0["href"] = "https://" + host + "/users/fedilogue"
	link1 := make(map[string]string)
	link1["rel"] = "self"
	link1["type"] = "application/activity+json"
	link1["href"] = "https://" + host + "/users/fedilogue"
	link2 := make(map[string]string)
	link2["rel"] = "self"
	link2["type"] = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
	link2["href"] = "https://" + host + "/users/fedilogue"
	link3 := make(map[string]string)
	link3["rel"] = "http://ostatus.org/schema/1.0/subscribe"
	link3["template"] = "https://" + host + "/ostatus_subscribe?acct={uri}"

	links := []map[string]string{link0, link1, link2, link3}
	webfingermap["links"] = links

	webfingerbin, err := json.Marshal(webfingermap)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	webfingerstr := string(webfingerbin)

	query := r.URL.Query()
	resourceRaw, exists := query["resource"]

	if exists {
		resource := resourceRaw[0]
		if resource != "acct:fedilogue@"+host {
			fmt.Println("Writes properly but wrong acct")
			w.Header().Set("Content-Type", "application/json; charset=utf-8")
			fmt.Fprintf(w, webfingerstr)
			return
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		fmt.Fprintf(w, webfingerstr)
	} else {
		fmt.Println(query)
		w.WriteHeader(http.StatusNotFound)
		return
	}
}

func inboxHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer r.Body.Close()

		var findtype RelayBase
		err = json.Unmarshal(body, &findtype)
		if err != nil {
			logWarn("Unable to unmarshal here, exiting...")
			return
		}

		switch findtype.Type {
		case "Create":
			var createobject CreateObject

			err = json.Unmarshal(body, &createobject)
			if err != nil {
				return
			}

			go check_activity(createobject.ID)
			slashend := strings.Index(createobject.ID[8:], "/")
			newinstance := createobject.ID[8 : 8+slashend]

			go CheckInstance(newinstance, "")

		case "Update":
		case "Reject":
		case "Add":
		case "Remove":
		case "Follow":
		case "Accept":
		case "Like":
		case "Announce":
			var announceobject AnnounceObject
			err = json.Unmarshal(body, &announceobject)
			if err != nil {
				fmt.Println(err.Error())
				return
			}
			check_activity(announceobject.Object)

			matchset := re.FindStringSubmatch(announceobject.Object)
			if matchset != nil {
				newinstance := matchset[1]
				go CheckInstance(newinstance, "")
			}
		case "Delete":
		case "Undo":
		default:
			logWarn("Unknown ActivityPub request:", findtype.Type)
		}

		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintf(w, "{}")
	}
}

func internalFetch(w http.ResponseWriter, r *http.Request) {

	publicKeyBytes, err := ioutil.ReadFile("keys/public.pem")
	publicKeyString := string(publicKeyBytes)
	publicKeyString = strings.Replace(publicKeyString, "\n", "\\n", -11)
	if err != nil {
		log.Fatal(err)
	}

	staticjson := fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://%[1]s/schemas/litepub-0.1.jsonld",{"@language":"und"}],"endpoints":{"oauthAuthorizationEndpoint":"https://%[1]s/oauth/authorize","oauthRegistrationEndpoint":"https://%[1]s/api/v1/apps","oauthTokenEndpoint":"https://%[1]s/oauth/token","sharedInbox":"https://%[1]s/inbox","uploadMedia":"https://%[1]s/api/ap/upload_media"},"followers":"https://%[1]s/internal/fetch/followers","following":"https://%[1]s/internal/fetch/following","id":"https://%[1]s/internal/fetch","inbox":"https://%[1]s/internal/fetch/inbox","invisible":true,"manuallyApprovesFollowers":false,"name":"Pleroma","preferredUsername":"fedilogue","publicKey":{"id":"https://%[1]s/internal/fetch#main-key","owner":"https://%[1]s/internal/fetch","publicKeyPem":"%[2]s"},"summary":"Fedilogue Key or something.","type":"Application","url":"https://%[1]s/internal/fetch"}`, r.Host, publicKeyString)

	fmt.Fprintf(w, staticjson)

}

func relay(w http.ResponseWriter, r *http.Request) {

	fmt.Println("Someone out there requested /relay")

	publicKeyBytes, err := ioutil.ReadFile("keys/public.pem")
	publicKeyString := string(publicKeyBytes)
	publicKeyString = strings.Replace(publicKeyString, "\n", "\\n", -11)
	if err != nil {
		log.Fatal(err)
	}

	staticjson := fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://%[1]s/schemas/litepub-0.1.jsonld",{"@language":"und"}],"alsoKnownAs":[],"attachment":[],"capabilities":{},"discoverable":false,"endpoints":{"oauthAuthorizationEndpoint":"https://%[1]s/oauth/authorize","oauthRegistrationEndpoint":"https://%[1]s/api/v1/apps","oauthTokenEndpoint":"https://%[1]s/oauth/token","sharedInbox":"https://%[1]s/inbox","uploadMedia":"https://%[1]s/api/ap/upload_media"},"featured":"https://%[1]s/relay/collections/featured","followers":"https://%[1]s/relay/followers","following":"https://%[1]s/relay/following","id":"https://%[1]s/relay","inbox":"https://%[1]s/relay/inbox","manuallyApprovesFollowers":false,"name":null,"outbox":"https://%[1]s/relay/outbox","preferredUsername":"relay","publicKey":{"id":"https://%[1]s/relay#main-key","owner":"https://%[1]s/relay","publicKeyPem":"%[2]s"},"summary":"","tag":[],"type":"Person","url":"https://%[1]s/relay"}`, r.Host, publicKeyString)

	fmt.Fprintf(w, staticjson)

}

func usersFedilogueFollowers(w http.ResponseWriter, r *http.Request) {
	fmt.Println("PATH --> ", r.URL.Path)
	host := r.Host
	contextlist := map[string]string{"@language": "und"}

	context := []interface{}{"https://www.w3.org/ns/activitystreams", "https://" + host + "/schemas/litepub-0.1.jsonld", contextlist}
	followersmap := make(map[string]interface{})
	followersmap["@context"] = context

	staticjson := "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://" + host + "/schemas/litepub-0.1.jsonld\",{\"@language\":\"und\"}],\"first\":{\"id\":\"https://" + host + "/users/fedilogue/followers?page=1\",\"next\":\"https://" + host + "/users/fedilogue/followers?page=2\",\"orderedItems\":[\"https://mastodon.host/users/federationbot\"],\"partOf\":\"https://" + host + "/users/fedilogue/followers\",\"totalItems\":1,\"type\":\"OrderedCollectionPage\"},\"id\":\"https://" + host + "/users/fedilogue/followers\",\"totalItems\":1,\"type\":\"OrderedCollection\"}"

	w.Header().Set("Content-Type", "application/activity+json; charset=utf-8")
	fmt.Fprintf(w, staticjson)
}

func usersFedilogueFollowing(w http.ResponseWriter, r *http.Request) {
	host := r.Host
	fmt.Println("PATH --> ", r.URL.Path)
	staticjson := "{\"@context\": [\"https://www.w3.org/ns/activitystreams\", \"https://" + host + "/schemas/litepub-0.1.jsonld\", {\"@language\": \"und\"}], \"first\": {\"id\": \"https://" + host + "/users/fedilogue/following?page=1\", \"orderedItems\": [], \"partOf\": \"https://" + host + "/users/fedilogue/following\", \"totalItems\": 0, \"type\": \"OrderedCollectionPage\"}, \"id\": \"https://" + host + "/users/fedilogue/following\", \"totalItems\": 0, \"type\": \"OrderedCollection\"}"
	w.Header().Set("Content-Type", "application/activity+json; charset=utf-8")
	fmt.Fprintf(w, staticjson)
}

func usersFedilogue(w http.ResponseWriter, r *http.Request) {
	fmt.Println("PATH --> ", r.URL.Path)
	host := r.Host

	fmt.Println(r.Host)
	fmt.Println(r.Header["Accept"])

	publickeybin, err := ioutil.ReadFile("keys/public.pem")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	publickeypemstr := string(publickeybin)

	publicKey := map[string]string{"id": "https://" + host + "/users/fedilogue#main-key", "owner": "https://" + host + "/users/fedilogue", "publicKeyPem": publickeypemstr}

	capabilities := map[string]bool{}
	tag := []string{}
	contextlist := map[string]string{"@language": "und"}
	attachment := []string{}
	endpoints := map[string]string{"oauthAuthorizationEndpoint": "https://" + host + "/oauth/authorize", "oauthRegistrationEndpoint": "https://" + host + "/api/v1/apps", "oauthTokenEndpoint": "https://" + host + "/oauth/token", "sharedInbox": "https://" + host + "/inbox", "uploadMedia": "https://" + host + "/api/ap/upload_media"}

	context := []interface{}{"https://www.w3.org/ns/activitystreams", "https://" + host + "/schemas/litepub-0.1.jsonld", contextlist}
	actorjsonmap := make(map[string]interface{})
	actorjsonmap["@context"] = context
	actorjsonmap["attachment"] = attachment
	actorjsonmap["capabilities"] = capabilities
	actorjsonmap["discoverable"] = false
	actorjsonmap["endpoints"] = endpoints
	actorjsonmap["followers"] = "https://" + host + "/users/fedilogue/followers"
	actorjsonmap["following"] = "https://" + host + "/users/fedilogue/following"
	actorjsonmap["id"] = "https://" + host + "/users/fedilogue"
	actorjsonmap["inbox"] = "https://" + host + "/users/fedilogue/inbox"
	actorjsonmap["manuallyApprovesFollowers"] = false
	actorjsonmap["name"] = "Fedilogue Mass Follower"
	actorjsonmap["outbox"] = "https://" + host + "/users/fedilogue/outbox"
	actorjsonmap["preferredUsername"] = "fedilogue"
	actorjsonmap["publicKey"] = publicKey
	actorjsonmap["summary"] = ""
	actorjsonmap["tag"] = tag
	actorjsonmap["type"] = "Application"
	actorjsonmap["uri"] = "https://" + host + "/users/fedilogue"

	actorjsonbin, err := json.Marshal(actorjsonmap)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	actorjsonstr := string(actorjsonbin)
	w.Header().Set("Content-Type", "application/activity+json; charset=utf-8")
	fmt.Fprintf(w, actorjsonstr)
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	for name, headers := range r.Header {
		fmt.Println("ROW: ", name, " ", headers)
		for _, h := range headers {
			fmt.Fprintf(w, "%v: %v\n", name, h)
		}
	}

	reqBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("POST DATA: %s\n", reqBody)

}

func webmain() {

	webreceiver := http.NewServeMux()

	webreceiver.HandleFunc("/.well-known/webfinger", webfinger)
	webreceiver.HandleFunc("/.well-known/host-meta", hostmeta)
	webreceiver.HandleFunc("/inbox", inboxHandler())
	webreceiver.HandleFunc("/internal/fetch", internalFetch)
	webreceiver.HandleFunc("/relay", relay)
	webreceiver.HandleFunc("/users/fedilogue", usersFedilogue)
	webreceiver.HandleFunc("/users/fedilogue/followers", usersFedilogueFollowers)
	webreceiver.HandleFunc("/users/fedilogue/following", usersFedilogueFollowing)
	webreceiver.HandleFunc("/", errorHandler)
	log.Print("Starting HTTP inbox on port 127.0.0.1:8042")
	log.Fatal(http.ListenAndServe("127.0.0.1:8042", webreceiver))
}