package common

import (
	"log"
	"net/http"
	"net/url"
)

const RequestType_Push = "push"
const RequestType_Repository = "repository"
const RequestType_PR = "pull_request"
const RequestType_PR_sync = "pull_request_sync"

type RequestProcessor func(*RequestHandler) error

type ListenDefinitions struct {
	GitAuthor, Url string
	Handlers       map[string]RequestProcessor
}

func StartServer(listenDefs ListenDefinitions) {
	StartServerWithAddress(listenDefs, "[::1]:8000")
}

func StartServerWithAddress(listenDefs ListenDefinitions, addr string) {
	if listenDefs.Url != url.PathEscape(listenDefs.Url) {
		log.Fatalf("Invalid Url fragment (%s) to listen on. Aborting", listenDefs.Url)
	}

	http.HandleFunc("/"+listenDefs.Url, func(res http.ResponseWriter, req *http.Request) {
		h := CreateRequestHandler(listenDefs.GitAuthor, listenDefs.Url)
		defer h.Close()

		if len(req.Header.Get("Content-Type")) == 0 ||
			req.Header["Content-Type"][0] != "application/json" ||
			req.Method != "POST" {

			h.WriteError()
			res.WriteHeader(http.StatusInternalServerError)
			return
		}

		hdr := req.Header[GiteaRequestHeader]
		if len(hdr) != 1 {
			h.ErrLogger.Printf("Unsupported number of %s headers: %d: %#v\n", GiteaRequestHeader, len(hdr), hdr)
			h.WriteError()
			res.WriteHeader(http.StatusInternalServerError)
			return
		}
		reqType := hdr[0]

		if handler, ok := listenDefs.Handlers[reqType]; ok {
			switch reqType {
			case RequestType_Repository:
				h.parseRepositoryRequest(req.Body)
			case RequestType_Push:
				h.parsePushRequest(req.Body)
			case RequestType_PR:
				h.parsePullRequest(req.Body)
			case RequestType_PR_sync:
				h.parsePullRequestSync(req.Body)
			default:
				h.ErrLogger.Printf("Unhandled request type: %s\n", reqType)
				res.WriteHeader(http.StatusInternalServerError)
				return
			}

			if h.HasError() {
				h.ErrLogger.Printf("error in parser %s: %v\n", reqType, h.Error)
				res.WriteHeader(http.StatusInternalServerError)
				return
			}

			if err := handler(h); err != nil {
				h.ErrLogger.Printf("error in handler for %s: %v\n", reqType, err)
				res.WriteHeader(http.StatusInternalServerError)
				return
			}
		} else {
			h.ErrLogger.Printf("Unsupported request type: %s\n", reqType)
			res.WriteHeader(http.StatusInternalServerError)
			return
		}

		if h.HasError() {
			h.WriteError()
			res.WriteHeader(http.StatusInternalServerError)
			return
		}
		res.Header().Add("Content-Type", "application/json")
		res.WriteHeader(http.StatusOK)
	})

	log.Fatal(http.ListenAndServe(addr, nil))
}
