package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/google/uuid"
	"github.com/RyanCopley/skybridge/user/internal/domain"
)

// UserClient provides an interface to interact with the user service
type UserClient struct {
	baseURL    string
	httpClient *http.Client
	userEmail  string // For authentication
}

// NewUserClient creates a new user service client
func NewUserClient(baseURL, userEmail string) *UserClient {
	return &UserClient{
		baseURL: baseURL,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
		userEmail: userEmail,
	}
}

// NewUserClientWithTimeout creates a new user service client with custom timeout
func NewUserClientWithTimeout(baseURL, userEmail string, timeout time.Duration) *UserClient {
	return &UserClient{
		baseURL: baseURL,
		httpClient: &http.Client{
			Timeout: timeout,
		},
		userEmail: userEmail,
	}
}

// CreateUser creates a new user
func (c *UserClient) CreateUser(req *domain.CreateUserRequest) (*domain.User, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := c.newRequest("POST", "/api/users", bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}

	var user domain.User
	err = c.doRequest(httpReq, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// GetUserByID retrieves a user by ID
func (c *UserClient) GetUserByID(id uuid.UUID) (*domain.User, error) {
	path := fmt.Sprintf("/api/users/%s", id.String())
	httpReq, err := c.newRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}

	var user domain.User
	err = c.doRequest(httpReq, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// GetUserByEmail retrieves a user by email
func (c *UserClient) GetUserByEmail(email string) (*domain.User, error) {
	path := fmt.Sprintf("/api/users/email/%s", url.PathEscape(email))
	httpReq, err := c.newRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}

	var user domain.User
	err = c.doRequest(httpReq, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// UpdateUser updates an existing user
func (c *UserClient) UpdateUser(id uuid.UUID, req *domain.UpdateUserRequest) (*domain.User, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	path := fmt.Sprintf("/api/users/%s", id.String())
	httpReq, err := c.newRequest("PUT", path, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}

	var user domain.User
	err = c.doRequest(httpReq, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// DeleteUser deletes a user by ID
func (c *UserClient) DeleteUser(id uuid.UUID) error {
	path := fmt.Sprintf("/api/users/%s", id.String())
	httpReq, err := c.newRequest("DELETE", path, nil)
	if err != nil {
		return err
	}

	return c.doRequest(httpReq, nil)
}

// ListUsers retrieves a list of users with optional filters
func (c *UserClient) ListUsers(req *domain.ListUsersRequest) (*domain.ListUsersResponse, error) {
	// Build query parameters
	params := url.Values{}
	
	if req.Status != nil {
		params.Set("status", string(*req.Status))
	}
	if req.Role != nil {
		params.Set("role", string(*req.Role))
	}
	if req.Search != "" {
		params.Set("search", req.Search)
	}
	if req.Limit > 0 {
		params.Set("limit", strconv.Itoa(req.Limit))
	}
	if req.Offset > 0 {
		params.Set("offset", strconv.Itoa(req.Offset))
	}
	if req.OrderBy != "" {
		params.Set("order_by", req.OrderBy)
	}
	if req.OrderDir != "" {
		params.Set("order_dir", req.OrderDir)
	}

	path := "/api/users"
	if len(params) > 0 {
		path += "?" + params.Encode()
	}

	httpReq, err := c.newRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}

	var response domain.ListUsersResponse
	err = c.doRequest(httpReq, &response)
	if err != nil {
		return nil, err
	}

	return &response, nil
}

// ExistsByEmail checks if a user exists with the given email
func (c *UserClient) ExistsByEmail(email string) (bool, error) {
	path := fmt.Sprintf("/api/users/exists/%s", url.PathEscape(email))
	httpReq, err := c.newRequest("GET", path, nil)
	if err != nil {
		return false, err
	}

	var response map[string]interface{}
	err = c.doRequest(httpReq, &response)
	if err != nil {
		return false, err
	}

	exists, ok := response["exists"].(bool)
	if !ok {
		return false, fmt.Errorf("invalid response format")
	}

	return exists, nil
}

// Health checks the health of the user service
func (c *UserClient) Health() (map[string]interface{}, error) {
	httpReq, err := c.newRequest("GET", "/health", nil)
	if err != nil {
		return nil, err
	}

	var response map[string]interface{}
	err = c.doRequest(httpReq, &response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

// newRequest creates a new HTTP request with authentication headers
func (c *UserClient) newRequest(method, path string, body io.Reader) (*http.Request, error) {
	url := c.baseURL + path
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	
	// Add authentication header
	if c.userEmail != "" {
		req.Header.Set("X-User-Email", c.userEmail)
	}

	return req, nil
}

// doRequest executes an HTTP request and handles the response
func (c *UserClient) doRequest(req *http.Request, target interface{}) error {
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response body: %w", err)
	}

	if resp.StatusCode >= 400 {
		var errorResponse map[string]interface{}
		if json.Unmarshal(body, &errorResponse) == nil {
			if errorMsg, ok := errorResponse["error"].(string); ok {
				return fmt.Errorf("API error (status %d): %s", resp.StatusCode, errorMsg)
			}
		}
		return fmt.Errorf("HTTP error (status %d): %s", resp.StatusCode, string(body))
	}

	// If target is nil, we don't need to unmarshal (e.g., for DELETE requests)
	if target == nil {
		return nil
	}

	if err := json.Unmarshal(body, target); err != nil {
		return fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return nil
}