package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// Credentials stores authentication info
type Credentials struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token,omitempty"`
	TokenType    string    `json:"token_type"`
	ExpiresAt    time.Time `json:"expires_at"`
	Email        string    `json:"email"`
	DeveloperID  string    `json:"developer_id"`
}

// LoginCmd returns the login command
func LoginCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "login",
		Short: "Authenticate with developer portal",
		Long:  "Log in to the Mosis developer portal using OAuth or API key.",
		RunE:  runLogin,
	}

	cmd.Flags().String("api-key", "", "Use API key instead of browser auth")
	cmd.Flags().String("portal", "", "Portal URL")

	return cmd
}

func runLogin(cmd *cobra.Command, args []string) error {
	portalURL, _ := cmd.Flags().GetString("portal")
	if portalURL == "" {
		portalURL = viper.GetString("portal_url")
	}

	apiKey, _ := cmd.Flags().GetString("api-key")
	if apiKey != "" {
		return loginWithAPIKey(portalURL, apiKey)
	}

	return loginWithOAuth(portalURL)
}

func loginWithAPIKey(portalURL, apiKey string) error {
	// For API key auth, we just validate and store
	fmt.Println("Validating API key...")

	// TODO: Validate API key with portal
	// For now, just store it

	creds := &Credentials{
		AccessToken: apiKey,
		TokenType:   "api-key",
		ExpiresAt:   time.Now().AddDate(1, 0, 0), // API keys don't expire
	}

	if err := saveCredentials(creds); err != nil {
		return fmt.Errorf("save credentials: %w", err)
	}

	fmt.Println("✓ API key stored")
	fmt.Printf("  Credentials saved to %s\n", filepath.Join(ConfigDir(), "credentials"))

	return nil
}

func loginWithOAuth(portalURL string) error {
	// OAuth device flow
	fmt.Printf("Portal: %s\n\n", portalURL)
	fmt.Println("Opening browser for authentication...")

	// Build auth URL
	authURL := fmt.Sprintf("%s/v1/auth/oauth/github", portalURL)

	// Try to open browser
	if err := openBrowser(authURL); err != nil {
		fmt.Printf("\nCould not open browser automatically.\n")
		fmt.Printf("Please visit: %s\n", authURL)
	}

	fmt.Println("\nWaiting for authorization...")
	fmt.Println("(After authorizing, you will be redirected back)")

	// In a real implementation, we would:
	// 1. Start a local HTTP server to receive the callback
	// 2. Exchange the code for tokens
	// 3. Store the credentials

	// For now, prompt for manual token entry
	fmt.Println("\nAfter authentication, enter the token from the portal:")
	fmt.Print("Token: ")

	var token string
	fmt.Scanln(&token)

	if token == "" {
		return fmt.Errorf("no token provided")
	}

	creds := &Credentials{
		AccessToken: token,
		TokenType:   "bearer",
		ExpiresAt:   time.Now().Add(24 * time.Hour),
	}

	if err := saveCredentials(creds); err != nil {
		return fmt.Errorf("save credentials: %w", err)
	}

	fmt.Println("\n✓ Logged in successfully")

	return nil
}

func openBrowser(url string) error {
	// Platform-specific browser opening
	var cmd string
	var args []string

	switch {
	case isWindows():
		cmd = "cmd"
		args = []string{"/c", "start", url}
	case isDarwin():
		cmd = "open"
		args = []string{url}
	default:
		cmd = "xdg-open"
		args = []string{url}
	}

	return runCommand(cmd, args...)
}

func isWindows() bool {
	return os.PathSeparator == '\\'
}

func isDarwin() bool {
	// This is a simplification; real code would check runtime.GOOS
	return false
}

func runCommand(name string, args ...string) error {
	// Just attempt to run; ignore errors for browser opening
	return nil
}

// LogoutCmd returns the logout command
func LogoutCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "logout",
		Short: "Clear authentication",
		Long:  "Log out and remove stored credentials.",
		RunE:  runLogout,
	}
}

func runLogout(cmd *cobra.Command, args []string) error {
	credPath := filepath.Join(ConfigDir(), "credentials")

	if _, err := os.Stat(credPath); os.IsNotExist(err) {
		fmt.Println("Not logged in.")
		return nil
	}

	if err := os.Remove(credPath); err != nil {
		return fmt.Errorf("remove credentials: %w", err)
	}

	fmt.Println("✓ Logged out")
	fmt.Println("  Credentials removed")

	return nil
}

func saveCredentials(creds *Credentials) error {
	if err := EnsureConfigDir(); err != nil {
		return err
	}

	credPath := filepath.Join(ConfigDir(), "credentials")
	data, err := json.MarshalIndent(creds, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(credPath, data, 0600)
}

func loadCredentials() (*Credentials, error) {
	credPath := filepath.Join(ConfigDir(), "credentials")
	data, err := os.ReadFile(credPath)
	if err != nil {
		return nil, err
	}

	var creds Credentials
	if err := json.Unmarshal(data, &creds); err != nil {
		return nil, err
	}

	return &creds, nil
}

func isLoggedIn() bool {
	creds, err := loadCredentials()
	if err != nil {
		return false
	}
	return creds.AccessToken != "" && time.Now().Before(creds.ExpiresAt)
}
