package bkosmi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
)

const (
	// Token expiry buffer - refresh if token expires within this window
	tokenExpiryBuffer = 5 * time.Minute
)

// AuthManager handles authentication with Kosmi
type AuthManager struct {
	email        string
	password     string
	token        string
	tokenExpiry  time.Time
	refreshToken string
	userID       string
	mu           sync.RWMutex
	log          *logrus.Entry
	httpClient   *http.Client
}

// NewAuthManager creates a new authentication manager
func NewAuthManager(email, password string, log *logrus.Entry) *AuthManager {
	return &AuthManager{
		email:      email,
		password:   password,
		log:        log,
		httpClient: &http.Client{Timeout: 10 * time.Second},
	}
}

// Login performs email/password authentication
// 
// NOTE: The actual login API format needs to be verified through monitoring.
// This implementation is based on common GraphQL patterns and may need adjustment.
func (a *AuthManager) Login() error {
	a.mu.Lock()
	defer a.mu.Unlock()

	a.log.Info("Logging in to Kosmi...")

	// Prepare login mutation
	// Based on common GraphQL patterns, likely something like:
	// mutation { login(email: "...", password: "...") { token user { id displayName } } }
	mutation := map[string]interface{}{
		"query": `mutation Login($email: String!, $password: String!) {
			login(email: $email, password: $password) {
				token
				refreshToken
				expiresIn
				user {
					id
					displayName
					username
				}
			}
		}`,
		"variables": map[string]interface{}{
			"email":    a.email,
			"password": a.password,
		},
	}

	jsonBody, err := json.Marshal(mutation)
	if err != nil {
		return &AuthError{
			Op:     "login",
			Reason: "failed to marshal request",
			Err:    err,
		}
	}

	req, err := http.NewRequest("POST", kosmiHTTPURL, bytes.NewReader(jsonBody))
	if err != nil {
		return &AuthError{
			Op:     "login",
			Reason: "failed to create request",
			Err:    err,
		}
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Referer", "https://app.kosmi.io/")
	req.Header.Set("User-Agent", userAgent)

	resp, err := a.httpClient.Do(req)
	if err != nil {
		return &AuthError{
			Op:     "login",
			Reason: "request failed",
			Err:    err,
		}
	}
	defer resp.Body.Close()

	if resp.StatusCode == 401 {
		return &AuthError{
			Op:     "login",
			Reason: "invalid credentials",
			Err:    ErrAuthFailed,
		}
	}

	if resp.StatusCode != 200 {
		return &AuthError{
			Op:     "login",
			Reason: fmt.Sprintf("HTTP %d", resp.StatusCode),
			Err:    ErrAuthFailed,
		}
	}

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return &AuthError{
			Op:     "login",
			Reason: "failed to parse response",
			Err:    err,
		}
	}

	// Extract token and user info
	if data, ok := result["data"].(map[string]interface{}); ok {
		if login, ok := data["login"].(map[string]interface{}); ok {
			if token, ok := login["token"].(string); ok {
				a.token = token
				
				// Extract refresh token if present
				if refreshToken, ok := login["refreshToken"].(string); ok {
					a.refreshToken = refreshToken
				}
				
				// Calculate token expiry
				if expiresIn, ok := login["expiresIn"].(float64); ok {
					a.tokenExpiry = time.Now().Add(time.Duration(expiresIn) * time.Second)
				} else {
					// Default to 24 hours if not specified
					a.tokenExpiry = time.Now().Add(24 * time.Hour)
				}
				
				// Extract user ID
				if user, ok := login["user"].(map[string]interface{}); ok {
					if userID, ok := user["id"].(string); ok {
						a.userID = userID
					}
					if displayName, ok := user["displayName"].(string); ok {
						a.log.Infof("Logged in as: %s", displayName)
					}
				}
				
				a.log.Info("✅ Login successful")
				return nil
			}
		}
	}

	// Check for GraphQL errors
	if errors, ok := result["errors"].([]interface{}); ok && len(errors) > 0 {
		if errObj, ok := errors[0].(map[string]interface{}); ok {
			if message, ok := errObj["message"].(string); ok {
				return &AuthError{
					Op:     "login",
					Reason: message,
					Err:    ErrAuthFailed,
				}
			}
		}
	}

	return &AuthError{
		Op:     "login",
		Reason: "no token in response",
		Err:    ErrAuthFailed,
	}
}

// GetToken returns a valid token, refreshing if necessary
func (a *AuthManager) GetToken() (string, error) {
	a.mu.RLock()
	
	// Check if token is still valid
	if a.token != "" && time.Now().Before(a.tokenExpiry.Add(-tokenExpiryBuffer)) {
		token := a.token
		a.mu.RUnlock()
		return token, nil
	}
	
	a.mu.RUnlock()

	// Token is expired or about to expire, refresh it
	if a.refreshToken != "" {
		if err := a.RefreshToken(); err != nil {
			a.log.Warnf("Token refresh failed, attempting re-login: %v", err)
			if err := a.Login(); err != nil {
				return "", err
			}
		}
	} else {
		// No refresh token, need to login again
		if err := a.Login(); err != nil {
			return "", err
		}
	}

	a.mu.RLock()
	defer a.mu.RUnlock()
	return a.token, nil
}

// RefreshToken renews the access token using the refresh token
func (a *AuthManager) RefreshToken() error {
	a.mu.Lock()
	defer a.mu.Unlock()

	if a.refreshToken == "" {
		return &AuthError{
			Op:     "token_refresh",
			Reason: "no refresh token available",
			Err:    ErrTokenExpired,
		}
	}

	a.log.Debug("Refreshing authentication token...")

	// Prepare refresh mutation
	mutation := map[string]interface{}{
		"query": `mutation RefreshToken($refreshToken: String!) {
			refreshToken(refreshToken: $refreshToken) {
				token
				refreshToken
				expiresIn
			}
		}`,
		"variables": map[string]interface{}{
			"refreshToken": a.refreshToken,
		},
	}

	jsonBody, err := json.Marshal(mutation)
	if err != nil {
		return &AuthError{
			Op:     "token_refresh",
			Reason: "failed to marshal request",
			Err:    err,
		}
	}

	req, err := http.NewRequest("POST", kosmiHTTPURL, bytes.NewReader(jsonBody))
	if err != nil {
		return &AuthError{
			Op:     "token_refresh",
			Reason: "failed to create request",
			Err:    err,
		}
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Referer", "https://app.kosmi.io/")
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.token))

	resp, err := a.httpClient.Do(req)
	if err != nil {
		return &AuthError{
			Op:     "token_refresh",
			Reason: "request failed",
			Err:    err,
		}
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return &AuthError{
			Op:     "token_refresh",
			Reason: fmt.Sprintf("HTTP %d", resp.StatusCode),
			Err:    ErrTokenExpired,
		}
	}

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return &AuthError{
			Op:     "token_refresh",
			Reason: "failed to parse response",
			Err:    err,
		}
	}

	// Extract new token
	if data, ok := result["data"].(map[string]interface{}); ok {
		if refresh, ok := data["refreshToken"].(map[string]interface{}); ok {
			if token, ok := refresh["token"].(string); ok {
				a.token = token
				
				// Update refresh token if provided
				if newRefreshToken, ok := refresh["refreshToken"].(string); ok {
					a.refreshToken = newRefreshToken
				}
				
				// Update expiry
				if expiresIn, ok := refresh["expiresIn"].(float64); ok {
					a.tokenExpiry = time.Now().Add(time.Duration(expiresIn) * time.Second)
				} else {
					a.tokenExpiry = time.Now().Add(24 * time.Hour)
				}
				
				a.log.Debug("✅ Token refreshed successfully")
				return nil
			}
		}
	}

	return &AuthError{
		Op:     "token_refresh",
		Reason: "no token in response",
		Err:    ErrTokenExpired,
	}
}

// Logout invalidates the current session
func (a *AuthManager) Logout() error {
	a.mu.Lock()
	defer a.mu.Unlock()

	if a.token == "" {
		return nil // Already logged out
	}

	a.log.Info("Logging out from Kosmi...")

	// Prepare logout mutation
	mutation := map[string]interface{}{
		"query": `mutation Logout {
			logout {
				ok
			}
		}`,
	}

	jsonBody, err := json.Marshal(mutation)
	if err != nil {
		a.log.Warnf("Failed to marshal logout request: %v", err)
		// Continue with local cleanup
	} else {
		req, err := http.NewRequest("POST", kosmiHTTPURL, bytes.NewReader(jsonBody))
		if err == nil {
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("Referer", "https://app.kosmi.io/")
			req.Header.Set("User-Agent", userAgent)
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.token))

			resp, err := a.httpClient.Do(req)
			if err != nil {
				a.log.Warnf("Logout request failed: %v", err)
			} else {
				resp.Body.Close()
				if resp.StatusCode != 200 {
					a.log.Warnf("Logout returned HTTP %d", resp.StatusCode)
				}
			}
		}
	}

	// Clear local state regardless of server response
	a.token = ""
	a.refreshToken = ""
	a.tokenExpiry = time.Time{}
	a.userID = ""

	a.log.Info("✅ Logged out")
	return nil
}

// IsAuthenticated checks if we have a valid token
func (a *AuthManager) IsAuthenticated() bool {
	a.mu.RLock()
	defer a.mu.RUnlock()
	return a.token != "" && time.Now().Before(a.tokenExpiry)
}

// GetUserID returns the authenticated user's ID
func (a *AuthManager) GetUserID() string {
	a.mu.RLock()
	defer a.mu.RUnlock()
	return a.userID
}

