package platform

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"
)

// 代铸 (proxy-mint) on the desktop. The device-authorization flow yields a bootstrap
// machine token (scope app:cdrop, no meta) that proves the user's identity but is not a
// cdrop-managed device. This call exchanges it for a managed device session bound to this
// device's stable device_id, so the desktop joins cdrop's unified device list and is
// managed exactly like a browser — the same model, not a separate native-only track.

// DeviceSessionResult is the managed device session minted by 代铸: the device tokens that
// supersede the bootstrap token, plus the verified identity (so a native client, which only
// ever sees nameless machine tokens, shows the real display name instead of the sub UUID).
type DeviceSessionResult struct {
	AccessToken  string
	RefreshToken string
	ExpiresIn    int
	DeviceID     string
	UserID       string
	Name         string
	Avatar       string
}

// MintDeviceSession POSTs to {apiBase}/api/auth/device-session with the bootstrap token as
// Bearer; cdrop vouches for the verified subject and has the broker mint (R2-idempotent by
// user+app+meta) a session bound to deviceID. Re-login with the same deviceID rotates the
// one session rather than piling up duplicates.
func MintDeviceSession(ctx context.Context, apiBase, bootstrapToken, deviceID, deviceName, deviceType string) (*DeviceSessionResult, error) {
	body, err := json.Marshal(map[string]string{
		"device_id":   deviceID,
		"device_name": deviceName,
		"device_type": deviceType,
	})
	if err != nil {
		return nil, fmt.Errorf("device-session: marshal: %w", err)
	}
	endpoint := strings.TrimRight(apiBase, "/") + "/api/auth/device-session"
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("device-session: build request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", "Bearer "+bootstrapToken)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("device-session: request: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("device-session: status %d", resp.StatusCode)
	}

	var dr struct {
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
		ExpiresIn    int    `json:"expires_in"`
		DeviceID     string `json:"device_id"`
		UserID       string `json:"user_id"`
		Name         string `json:"name"`
		Avatar       string `json:"avatar"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&dr); err != nil {
		return nil, fmt.Errorf("device-session: decode: %w", err)
	}
	if dr.AccessToken == "" {
		return nil, errors.New("device-session: response missing access token")
	}
	return &DeviceSessionResult{
		AccessToken:  dr.AccessToken,
		RefreshToken: dr.RefreshToken,
		ExpiresIn:    dr.ExpiresIn,
		DeviceID:     dr.DeviceID,
		UserID:       dr.UserID,
		Name:         dr.Name,
		Avatar:       dr.Avatar,
	}, nil
}
