package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/jackc/pgx/v4"
)

var trendingexport string

func enableCors(w *http.ResponseWriter) {
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
}

func gettrends() {
	var err error
	var rows pgx.Rows

	for {
		rows, err = pool.Query(context.Background(), "SELECT word, ndoc FROM ts_stat($$ SELECT normalized_tsvector FROM activities WHERE activities.identifiedat > current_timestamp - interval '60 minutes' $$) ORDER BY ndoc DESC LIMIT 10")
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		trenditems := make([]interface{}, 0)
		//fmt.Println(trenditems)

		for rows.Next() {
			var word string
			var ndoc int

			err = rows.Scan(&word, &ndoc)
			if err != nil {
				panic(err)
			}

			trenditem := make(map[string]interface{})
			trenditem["ndoc"] = ndoc
			trenditem["word"] = word
			trenditems = append(trenditems, trenditem)
		}

		totalJson := make(map[string]interface{})
		totalJson["trends"] = trenditems

		data, err := json.Marshal(totalJson)
		if err != nil {
			log.Fatal("error marshaling combined activity: %v", err)
		}

		trendingexport = string(data)

		time.Sleep(time.Second * 60)
	}
}

func trending(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)

	if strings.Contains(r.Header.Get("Accept"), "application/json") {
		log.Print("Treat as an Json")
	} else {
		log.Println("Treat as HTML")
	}

	fmt.Fprintf(w, "%s", trendingexport)
}

func search(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	searchkeys, exists_search := r.URL.Query()["s"]
	offsetkeys, exists_offset := r.URL.Query()["o"]

	if strings.Contains(r.Header.Get("Accept"), "application/json") {
		log.Print("Treat as an Json")
	} else {
		log.Println("Treat as HTML")
	}

	var err error
	var rows pgx.Rows
	var searchKey string
	if exists_search {
		searchKey = searchkeys[0]
	}
	var offsetKey int
	if exists_offset {
		offsetKey, _ = strconv.Atoi(offsetkeys[0])
	} else {
		offsetKey = -1
	}

	if exists_search && searchKey != "" {
		if offsetKey == -1 {
			rows, err = pool.Query(context.Background(), "SELECT activities.id, activities.document, actors.document FROM activities as activities INNER JOIN actors as actors ON activities.document->>'actor' = actors.document->>'id' WHERE activities.normalized_tsvector @@ plainto_tsquery($1) ORDER BY activities.id DESC LIMIT 10", searchKey)
		} else {
			rows, err = pool.Query(context.Background(), "SELECT activities.id, activities.document, actors.document FROM activities as activities INNER JOIN actors as actors ON activities.document->>'actor' = actors.document->>'id' WHERE activities.normalized_tsvector @@ plainto_tsquery($1) AND activities.id < $2 ORDER BY activities.id DESC LIMIT 10", searchKey, offsetKey)
		}
	} else {
		if offsetKey == -1 {
			rows, err = pool.Query(context.Background(), "SELECT activities.id, activities.document, actors.document FROM activities as activities INNER JOIN actors as actors ON activities.document->>'actor' = actors.document->>'id' ORDER BY activities.id DESC LIMIT 10")
		} else {
			rows, err = pool.Query(context.Background(), "SELECT activities.id, activities.document, actors.document FROM activities as activities INNER JOIN actors as actors ON activities.document->>'actor' = actors.document->>'id' AND activities.id < $1 ORDER BY activities.id DESC LIMIT 10", offsetKey)
		}
	}

	if err != nil {
		panic(err)
	}
	defer rows.Close()

	var earliestid int
	earliestid = 0
	var activitiesJson []map[string]json.RawMessage
	for rows.Next() {
		var id int
		var activityRaw string
		var actorRaw string
		var activityJson map[string]json.RawMessage

		err = rows.Scan(&id, &activityRaw, &actorRaw)
		if err != nil {
			panic(err)
		}

		err := json.Unmarshal([]byte(activityRaw), &activityJson)
		if err != nil {
			fmt.Println(err)
		}
		if earliestid == 0 {
			earliestid = id
		} else if earliestid > id {
			earliestid = id
		}

		activityJson["actor"] = json.RawMessage(actorRaw)
		activitiesJson = append(activitiesJson, activityJson)
	}

	requestData := make(map[string]int)
	requestData["earliestid"] = earliestid

	totalJson := make(map[string]interface{})
	totalJson["requestdata"] = requestData
	totalJson["activities"] = activitiesJson

	data, err := json.Marshal(totalJson)
	if err != nil {
		log.Fatal("error marshaling combined activity: %v", err)
	}
	fmt.Fprintf(w, "%s", data)
}

func main() {
	pool = getDbPool()

	go gettrends()

	http.HandleFunc("/search", search)
	http.HandleFunc("/trending", trending)
	log.Print("Starting HTTP inbox on port http://0.0.0.0:6431")
	log.Fatal(http.ListenAndServe("0.0.0.0:6431", nil))
}