package httprule

import (
	"fmt"
	"net/http"

	"google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
)

func Get(m protoreflect.MethodDescriptor) (*annotations.HttpRule, bool) {
	descriptor, ok := proto.GetExtension(m.Options(), annotations.E_Http).(*annotations.HttpRule)
	if !ok || descriptor == nil {
		return nil, false
	}
	return descriptor, true
}

type Rule struct {
	// The HTTP method to use.
	Method string
	// The template describing the URL to use.
	Template        Template
	Body            string
	AdditionalRules []Rule
}

func ParseRule(httpRule *annotations.HttpRule) (Rule, error) {
	method, err := httpRuleMethod(httpRule)
	if err != nil {
		return Rule{}, err
	}
	url, err := httpRuleURL(httpRule)
	if err != nil {
		return Rule{}, err
	}
	template, err := ParseTemplate(url)
	if err != nil {
		return Rule{}, err
	}
	additional := make([]Rule, len(httpRule.GetAdditionalBindings()))
	for i, r := range httpRule.GetAdditionalBindings() {
		a, err := ParseRule(r)
		if err != nil {
			return Rule{}, fmt.Errorf("parse additional binding %d: %w", i, err)
		}
		additional[i] = a
	}
	return Rule{
		Method:          method,
		Template:        template,
		Body:            httpRule.GetBody(),
		AdditionalRules: additional,
	}, nil
}

func httpRuleURL(rule *annotations.HttpRule) (string, error) {
	switch v := rule.GetPattern().(type) {
	case *annotations.HttpRule_Get:
		return v.Get, nil
	case *annotations.HttpRule_Post:
		return v.Post, nil
	case *annotations.HttpRule_Delete:
		return v.Delete, nil
	case *annotations.HttpRule_Patch:
		return v.Patch, nil
	case *annotations.HttpRule_Put:
		return v.Put, nil
	case *annotations.HttpRule_Custom:
		return v.Custom.GetPath(), nil
	default:
		return "", fmt.Errorf("http rule does not have an URL defined")
	}
}

func httpRuleMethod(rule *annotations.HttpRule) (string, error) {
	switch v := rule.GetPattern().(type) {
	case *annotations.HttpRule_Get:
		return http.MethodGet, nil
	case *annotations.HttpRule_Post:
		return http.MethodPost, nil
	case *annotations.HttpRule_Delete:
		return http.MethodDelete, nil
	case *annotations.HttpRule_Patch:
		return http.MethodPatch, nil
	case *annotations.HttpRule_Put:
		return http.MethodPut, nil
	case *annotations.HttpRule_Custom:
		return v.Custom.GetKind(), nil
	default:
		return "", fmt.Errorf("http rule does not have an URL defined")
	}
}