package server

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"reflect"
	"runtime/debug"
	"strings"

	"repositories.action2quare.com/ayo/go-ayo/logger"
)

const (
	defaultMaxMemory = 32 << 10 // 32 KB
)

type HoustonServerWithHandler interface {
	HoustonServer
	RegisterHandlers(serveMux *http.ServeMux, prefix string) error
}

type houstonHandler struct {
	HoustonServer
	methods map[string]reflect.Method
}

func NewHoustonHandler() HoustonServerWithHandler {
	var tmp *houstonHandler

	methods := make(map[string]reflect.Method)
	tp := reflect.TypeOf(tmp)
	for i := 0; i < tp.NumMethod(); i++ {
		method := tp.Method(i)
		methods[strings.ToLower(method.Name)] = method
	}
	return &houstonHandler{
		HoustonServer: NewServer(),
		methods:       methods,
	}
}

func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string) error {
	serveMux.Handle("/"+path.Join(prefix, "houston"), h)

	dfsx := http.FileServer(http.Dir("./deploys"))
	serveMux.Handle(
		"/"+path.Join(prefix, "deploys", "/"),
		http.StripPrefix(fmt.Sprintf("/%s/deploys", prefix), dfsx))

	ufsx := http.FileServer(http.Dir("./downloads"))
	serveMux.Handle(
		"/"+path.Join(prefix, "downloads", "/"),
		http.StripPrefix(fmt.Sprintf("/%s/downloads", prefix), ufsx))

	serveMux.HandleFunc("/"+path.Join(prefix, "upload"), func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			s := recover()
			if s != nil {
				logger.Println(s)
				debug.PrintStack()
			}
			io.Copy(io.Discard, r.Body)
			r.Body.Close()
		}()

		name := r.Header.Get("Houston-Service-Name")
		version := r.Header.Get("Houston-Service-Version")
		filename := r.Header.Get("Houston-Service-Filename")
		dir := fmt.Sprintf("./downloads/%s/%s", name, version)
		if err := os.MkdirAll(dir, os.ModePerm); err == nil {
			file, _ := os.Create(path.Join(dir, filename))
			if file != nil {
				defer file.Close()
				if _, err = io.Copy(file, r.Body); err != nil {
					w.WriteHeader(http.StatusInternalServerError)
				}
			} else {
				w.WriteHeader(http.StatusInternalServerError)
			}
		} else {
			w.WriteHeader(http.StatusInternalServerError)
		}
	})

	return nil
}

func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		s := recover()
		if s != nil {
			logger.Println(s)
			debug.PrintStack()
		}
	}()

	defer func() {
		io.Copy(io.Discard, r.Body)
		r.Body.Close()
	}()

	var operation string
	if r.Method == "POST" {
		operation = r.FormValue("operation")
	} else {
		operation = r.URL.Query().Get("operation")
	}

	if len(operation) == 0 {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	method, ok := h.methods[strings.ToLower(operation)]
	if !ok {
		// 없는 operation
		logger.Println("fail to call api. operation is not valid :", operation)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if r.PostForm == nil {
		r.ParseMultipartForm(defaultMaxMemory)
	}

	args := []reflect.Value{
		reflect.ValueOf(h),
		reflect.ValueOf(w),
		reflect.ValueOf(r),
	}

	method.Func.Call(args)
}
