package proxy

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/google/uuid"

	"github.com/fujin/anthropic-proxy/internal/auth"
)

const messagesURL = "https://api.anthropic.com/v1/messages?beta=true"

type UpstreamClient struct {
	client    http.Client
	sessionID string
	profile   *SniffedProfile
}

func NewUpstreamClient(profile *SniffedProfile) *UpstreamClient {
	return &UpstreamClient{
		client: http.Client{
			Timeout:   0,
			Transport: newUtlsRoundTripper(),
		},
		sessionID: uuid.New().String(),
		profile:   profile,
	}
}

func (u *UpstreamClient) version() string {
	if u.profile != nil && u.profile.Version != "" {
		return u.profile.Version
	}
	return "2.1.92"
}

// applyHeaders replays sniffed headers, substituting auth + per-request IDs + accept.
func (u *UpstreamClient) applyHeaders(req *http.Request, token string, streaming bool) {
	if u.profile != nil {
		for _, h := range u.profile.Headers {
			req.Header.Set(h[0], h[1])
		}
	}

	req.Header.Del("Authorization")
	req.Header.Del("x-api-key")
	if strings.HasPrefix(token, "sk-ant-oat") {
		req.Header.Set("Authorization", "Bearer "+token)
	} else {
		req.Header.Set("x-api-key", token)
	}

	req.Header.Set("X-Claude-Code-Session-Id", u.sessionID)
	req.Header.Set("x-client-request-id", uuid.New().String())

	if streaming {
		req.Header.Set("Accept", "text/event-stream")
	} else {
		req.Header.Set("Accept", "application/json")
	}
	req.Header.Set("Accept-Encoding", "identity")
}

func (u *UpstreamClient) Execute(ctx context.Context, cred *auth.Credential, body []byte) ([]byte, http.Header, int, error) {
	body = injectBillingHeader(body, u.version())

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, messagesURL, bytes.NewReader(body))
	if err != nil {
		return nil, nil, 0, fmt.Errorf("build upstream request: %w", err)
	}
	u.applyHeaders(req, cred.Token(), false)

	resp, err := u.client.Do(req)
	if err != nil {
		return nil, nil, 0, fmt.Errorf("upstream request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, resp.StatusCode, fmt.Errorf("read upstream response: %w", err)
	}
	return respBody, resp.Header, resp.StatusCode, nil
}

func (u *UpstreamClient) ExecuteStream(ctx context.Context, cred *auth.Credential, body []byte) (*http.Response, error) {
	body = injectBillingHeader(body, u.version())

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, messagesURL, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("build upstream stream request: %w", err)
	}
	u.applyHeaders(req, cred.Token(), true)

	resp, err := u.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("upstream stream request: %w", err)
	}
	return resp, nil
}
