package http

import (
	"bufio"
	"context"
	"encoding/base64"
	"fmt"
	"github.com/google/uuid"
	"io"
	"net"
	"net/textproto"
	"net/url"
	"proxy-server/gateway/core"
	"proxy-server/gateway/fwd/auth"
	"strings"

	"errors"
)

type Request struct {
	conn    net.Conn
	reader  *bufio.Reader
	method  string
	uri     string
	proto   string
	headers *textproto.MIMEHeader
	auth    *core.AuthContext
	dest    *core.FwdAddr
}

func Process(ctx context.Context, conn net.Conn) (*core.Conn, error) {
	reader := bufio.NewReader(conn)
	textReader := textproto.NewReader(reader)

	// 首行
	line, err := textReader.ReadLine()
	if err != nil {
		return nil, err
	}
	parts := strings.Fields(line)
	if len(parts) != 3 {
		return nil, errors.New("无效的 http 请求")
	}

	// 请求头
	headers, err := textReader.ReadMIMEHeader()
	if err != nil {
		return nil, fmt.Errorf("解析请求头失败: %v", err)
	}

	// 验证账号
	authInfo := headers.Get("Proxy-Authorization")
	var username *string = nil
	var password *string = nil
	if authInfo != "" {
		authParts := strings.Split(authInfo, " ")
		if len(authParts) != 2 {
			return nil, errors.New("无效的 Proxy-Authorization")
		}
		if authParts[0] != "Basic" {
			return nil, errors.New("不支持的认证方式")
		}
		authBytes, err := base64.URLEncoding.DecodeString(authParts[1])
		if err != nil {
			return nil, fmt.Errorf("解码认证信息失败: %v", err)
		}
		authPair := strings.Split(string(authBytes), ":")
		username = &authPair[0]
		password = &authPair[1]
	}

	authCtx, err := auth.Protect(conn, auth.Http, username, password)
	if err != nil {
		_, err = conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\n\r\n"))
		if err != nil {
			return nil, fmt.Errorf("响应 407 失败: %v", err)
		}
		return nil, fmt.Errorf("验证账号失败: %v", err)
	}

	// 获取 Host
	host := headers.Get("Host")
	if host == "" {
		return nil, errors.New("无效的 Host")
	}
	if !strings.Contains(host, ":") {
		host += ":80"
	}
	addr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return nil, fmt.Errorf("解析 Host 失败: %v", err)
	}

	request := &Request{
		conn:    conn,
		reader:  reader,
		method:  parts[0],
		uri:     parts[1],
		proto:   parts[2],
		headers: &headers,
		dest: &core.FwdAddr{
			IP:     addr.IP,
			Port:   addr.Port,
			Domain: host,
		},
		auth: authCtx,
	}

	var user *core.Conn
	if parts[0] == "CONNECT" {
		user, err = processHttps(ctx, request)
		if err != nil {
			return nil, err
		}
	} else {
		user, err = processHttp(ctx, request)
		if err != nil {
			return nil, err
		}
	}

	return user, nil
}

func processHttps(ctx context.Context, req *Request) (*core.Conn, error) {

	// 响应 CONNECT
	_, err := req.conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))
	if err != nil {
		return nil, fmt.Errorf("响应 CONNECT 失败: %v", err)
	}

	return &core.Conn{
		Conn:     req.conn,
		Reader:   req.reader,
		Tag:      uuid.New(),
		Protocol: "http",
		Dest:     req.dest,
		Auth:     req.auth,
	}, nil
}

func processHttp(ctx context.Context, req *Request) (*core.Conn, error) {

	// 修改请求头
	rawUrl, err := url.Parse(req.uri)
	if err != nil {
		return nil, fmt.Errorf("解析请求地址失败: %v", err)
	}
	rawUrl.Scheme = ""
	rawUrl.Host = ""
	req.uri = rawUrl.String()
	req.headers.Del("Proxy-Authorization")

	// 构造请求
	sb := strings.Builder{}

	sb.WriteString(req.method)
	sb.WriteString(" ")
	sb.WriteString(req.uri)
	sb.WriteString(" ")
	sb.WriteString(req.proto)
	sb.WriteString("\r\n")

	for k, v := range *req.headers {
		sb.WriteString(k)
		sb.WriteString(": ")
		sb.WriteString(strings.Join(v, ","))
		sb.WriteString("\r\n")
	}

	sb.WriteString("\r\n")

	mReader := io.MultiReader(strings.NewReader(sb.String()), req.conn)
	newReader := bufio.NewReader(mReader)

	return &core.Conn{
		Conn:     req.conn,
		Reader:   newReader,
		Tag:      uuid.New(),
		Protocol: "http",
		Dest:     req.dest,
		Auth:     req.auth,
	}, nil
}
