package server

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/middleware/logging"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/middleware/validate"
	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/gorilla/handlers"
	http2 "net/http"
	"time"
	v1 "voucher/api/v1"
	"voucher/internal/conf"
	log2 "voucher/internal/pkg/log"
	"voucher/internal/service"
)

// NewHTTPServer new an HTTP server.
func NewHTTPServer(
	c *conf.Bootstrap,
	log *log.Helper,
	accessLogger *log2.AccessLogger,
	cmb *service.CmbService,
	tripartiteService *service.TripartiteService,
	notifyService *service.NotifyService,
) *http.Server {
	//构建 server
	srv := buildHTTPServer(c, accessLogger, log)

	//srv.Handle("/voucher/debug/pprof/", pprof.NewHandler())

	srv.Route("/voucher/").GET("ping", func(ctx http.Context) error {
		return ctx.String(http2.StatusOK, "pong")
	})

	// 启星(启星-蓝色兄弟立减金代配) /voucher/qixing/v1/notify
	srv.Route("/voucher/").POST("qixing/v1/notify", tripartiteService.QiXingNotify)
	// 通知url必须为公网可访问的URL，必须为HTTPS，不能携带参数
	// https://域名.cn/voucher/v1/notify/123456 123456为微信主体商户号 /voucher/v1/notify/1100040695
	// https://gateway.dev.cdlsxd.cn/voucher/v1/notify/123456 123456为微信主体商户号 测试环境
	// https://voucher.86698.cn/voucher/v1/notify/123456 123456为微信主体商户号 正式环境
	srv.Route("/voucher/").POST("/v1/notify/{mch_id}", notifyService.Notify)
	srv.Route("/voucher/").POST("/v1/notifyMock", notifyService.NotifyMock)

	// ---脚本--
	// 订单通知重试 -- 不健全
	srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry)
	// 重试订单通知,查询微信状态再通知下游招行
	srv.Route("/voucher/").POST("retryOrderNotice", cmb.RetryOrderNotice)
	// 重试通知
	srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry)
	// 查询订单状态及微信状态
	srv.Route("/voucher/").POST("queryOrder/{order_no}/{phone}/{is_notice}", cmb.QueryOrder)
	srv.Route("/voucher/").GET("query-order/{order_no}/{phone}/{is_notice}", cmb.QueryOrder)
	// 查询商品
	srv.Route("/voucher/").POST("queryStock/{product_no}", cmb.QueryStock)
	// 注册商品tag到刚哥那边
	srv.Route("/voucher/").POST("registerTag/{id}", cmb.RegisterTag)
	// 成功订单查询最终状态处理
	srv.Route("/voucher/").POST("pushWechatQuery", cmb.PushWechatQuery)
	// 订单查询最终状态处理
	srv.Route("/voucher/").POST("timeSliceQueryPush", cmb.TimeSliceQueryPush)
	srv.Route("/voucher/").POST("pushWechatRetry/{batch_no}", cmb.PushWechatRetry)
	// 商品预警通知
	srv.Route("/voucher/").POST("warningBudget/{id}", cmb.WarningBudget)
	// 指定重复通知对应单子数据
	srv.Route("/voucher/").POST("specifyNotification", cmb.SpecifyNotification)
	// 订单使用通知下游
	srv.Route("/voucher/").POST("UsedNotifyPush", cmb.UsedNotifyPush)

	v1.RegisterCmbHTTPServer(srv, cmb)

	return srv
}

// buildHTTPServer 构建 HTTP Server
func buildHTTPServer(c *conf.Bootstrap, accessLogger *log2.AccessLogger, log *log.Helper) *http.Server {
	var opts []http.ServerOption

	if c.Server.Http.Network != "" {
		opts = append(opts, http.Network(c.Server.Http.Network))
	}

	if c.Server.Http.Addr != "" {
		opts = append(opts, http.Address(c.Server.Http.Addr))
	}

	if c.Server.Http.Timeout != nil {
		opts = append(opts, http.Timeout(30*time.Second))
	}

	// 允许跨域
	filters := []http.FilterFunc{handlers.CORS(
		// 域名配置
		handlers.AllowedOrigins([]string{"*"}),
		handlers.AllowedHeaders([]string{"Content-Type", "X-Requested-With", "Authorization"}),
		handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS", "DELETE"}),
	)}

	if c.Server.Http.IsResponseReqHeaders {
		//将请求的 header 头信息返回给客户端
		filters = append(filters, responseReqHeadersHandler)
	}

	opts = append(opts, http.Filter(filters...))

	//拦截恢复异常
	middlewares := []middleware.Middleware{
		recovery.Recovery(
			recovery.WithHandler(func(ctx context.Context, req, err interface{}) error {
				//做一些panic处理
				newErr, isOk := err.(*errors.Error)
				if isOk {
					//如果是自己定义的类型，则直接抛出去，目的是支持 panic(myErr) 类型的写法，提升开发速度
					return newErr
				}
				log.Errorf("系统错误，req=%+v, error=%+v", req, err)
				return fmt.Errorf("系统错误，req=%+v, error=%+v", req, err)
			}),
		),
	}

	//加入 validator
	middlewares = append(middlewares, validate.Validator())

	//打印访问日志
	if accessLogger != nil {
		middlewares = append(middlewares, logging.Server(*accessLogger))
	}

	opts = append(opts, http.Middleware(middlewares...))
	opts = append(opts, http.ResponseEncoder(responseEncoder))
	opts = append(opts, http.ErrorEncoder(errorEncoder()))
	//注册 service到 http 上
	srv := http.NewServer(opts...)

	//注册 openapi
	if c.Server.Http.GetIsOpenSwagger() {
		srv.HandlePrefix("/doc/", http2.StripPrefix("/doc/", http2.FileServer(http2.Dir("./third_party/swagger_ui"))))
	}

	return srv
}

// 将请求头响应到 header 中
func responseReqHeadersHandler(h http2.Handler) http2.Handler {
	return http2.HandlerFunc(func(w http2.ResponseWriter, r *http2.Request) {
		reqHeaders, _ := json.Marshal(r.Header)
		w.Header().Add("Req-Headers", string(reqHeaders))
		h.ServeHTTP(w, r)
	})
}

func responseEncoder(w http.ResponseWriter, r *http.Request, data interface{}) error {
	if data == nil {
		return nil
	}

	codec, _ := http.CodecForRequest(r, "Accept")
	dataByte, err := codec.Marshal(data)
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", fmt.Sprintf("application/%s", codec.Name()))
	_, err = w.Write(dataByte)

	return err
}

func errorEncoder() http.EncodeErrorFunc {
	return func(w http2.ResponseWriter, r *http2.Request, err error) {
		var message string
		var code int32

		se := errors.FromError(err)
		message = se.Message
		code = se.Code

		w.Header().Set("Content-Type", "application/json")
		switch code {
		case http2.StatusUnauthorized:
			w.WriteHeader(http2.StatusUnauthorized)
		default:
			w.WriteHeader(http2.StatusOK)
		}
		respBody := map[string]interface{}{
			"code":    code,
			"data":    "",
			"message": message,
		}
		body, _ := json.Marshal(respBody)
		_, _ = w.Write(body)
	}
}
