package cmd

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

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

	"omixlab.com/mosis-portal/pkg/mospkg"
)

// KeysCmd returns the keys command
func KeysCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "keys",
		Short: "Manage signing keys",
	}

	cmd.AddCommand(keysGenerateCmd())
	cmd.AddCommand(keysListCmd())
	cmd.AddCommand(keysRegisterCmd())

	return cmd
}

func keysGenerateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "generate",
		Short: "Generate Ed25519 signing keypair",
		RunE:  runKeysGenerate,
	}

	cmd.Flags().StringP("output", "o", "", "Output directory (default: ~/.mosis)")
	cmd.Flags().Bool("force", false, "Overwrite existing keys")

	return cmd
}

func runKeysGenerate(cmd *cobra.Command, args []string) error {
	outputDir, _ := cmd.Flags().GetString("output")
	if outputDir == "" {
		outputDir = ConfigDir()
	}

	privateKeyPath := filepath.Join(outputDir, "signing_key.pem")
	publicKeyPath := filepath.Join(outputDir, "signing_key.pub")

	// Check if keys exist
	force, _ := cmd.Flags().GetBool("force")
	if !force {
		if _, err := os.Stat(privateKeyPath); err == nil {
			return fmt.Errorf("signing key already exists at %s\n\nUse --force to overwrite", privateKeyPath)
		}
	}

	fmt.Println("Generating Ed25519 keypair...")

	// Generate keypair
	keyPair, err := mospkg.GenerateKeyPair()
	if err != nil {
		return fmt.Errorf("generate keypair: %w", err)
	}

	// Ensure directory exists
	if err := os.MkdirAll(outputDir, 0700); err != nil {
		return fmt.Errorf("create directory: %w", err)
	}

	// Save private key
	privatePEM, err := keyPair.PrivateKeyPEM()
	if err != nil {
		return fmt.Errorf("encode private key: %w", err)
	}
	if err := os.WriteFile(privateKeyPath, privatePEM, 0600); err != nil {
		return fmt.Errorf("save private key: %w", err)
	}

	// Save public key
	publicPEM, err := keyPair.PublicKeyPEM()
	if err != nil {
		return fmt.Errorf("encode public key: %w", err)
	}
	if err := os.WriteFile(publicKeyPath, publicPEM, 0644); err != nil {
		return fmt.Errorf("save public key: %w", err)
	}

	fingerprint := keyPair.Fingerprint()

	fmt.Printf("\nPrivate key saved to: %s\n", privateKeyPath)
	fmt.Printf("Public key saved to: %s\n", publicKeyPath)
	fmt.Printf("\nFingerprint: %s\n", fingerprint)

	fmt.Println(`
⚠ IMPORTANT: Keep your private key secure!
  - Never share or commit signing_key.pem
  - Back it up securely
  - If compromised, revoke immediately

Next step: Register your public key
  mosis keys register`)

	return nil
}

func keysListCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "list",
		Short: "List registered signing keys",
		RunE:  runKeysList,
	}
}

type RegisteredKey struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Fingerprint string    `json:"fingerprint"`
	CreatedAt   time.Time `json:"created_at"`
	LastUsed    time.Time `json:"last_used,omitempty"`
}

func runKeysList(cmd *cobra.Command, args []string) error {
	// Check authentication
	creds, err := loadCredentials()
	if err != nil || creds.AccessToken == "" {
		return fmt.Errorf("not logged in\n\nRun 'mosis login' first")
	}

	portalURL := viper.GetString("portal_url")

	// Fetch keys from portal
	keys, err := fetchRegisteredKeys(portalURL, creds.AccessToken)
	if err != nil {
		// If portal not reachable, show local key info
		fmt.Println("Could not connect to portal. Showing local key:")
		return showLocalKey()
	}

	if len(keys) == 0 {
		fmt.Println("No signing keys registered.")
		fmt.Println("\nGenerate and register a key:")
		fmt.Println("  mosis keys generate")
		fmt.Println("  mosis keys register")
		return nil
	}

	fmt.Println("Registered signing keys:\n")
	for _, key := range keys {
		fmt.Printf("  %s\n", key.Name)
		fmt.Printf("    ID: %s\n", key.ID)
		fmt.Printf("    Fingerprint: %s\n", key.Fingerprint)
		fmt.Printf("    Created: %s\n", key.CreatedAt.Format("Jan 2, 2006"))
		if !key.LastUsed.IsZero() {
			fmt.Printf("    Last used: %s\n", key.LastUsed.Format("Jan 2, 2006"))
		}
		fmt.Println()
	}

	return nil
}

func showLocalKey() error {
	publicKeyPath := filepath.Join(ConfigDir(), "signing_key.pub")
	data, err := os.ReadFile(publicKeyPath)
	if err != nil {
		fmt.Println("No local signing key found.")
		fmt.Println("\nGenerate a key with: mosis keys generate")
		return nil
	}

	publicKey, err := mospkg.LoadPublicKey(data)
	if err != nil {
		return fmt.Errorf("invalid public key: %w", err)
	}

	fingerprint := mospkg.PublicKeyFingerprint(publicKey)
	fmt.Printf("\nLocal key:\n")
	fmt.Printf("  Path: %s\n", publicKeyPath)
	fmt.Printf("  Fingerprint: %s\n", fingerprint)

	return nil
}

func fetchRegisteredKeys(portalURL, token string) ([]RegisteredKey, error) {
	url := fmt.Sprintf("%s/v1/signing-keys", portalURL)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("API error: %d", resp.StatusCode)
	}

	var keys []RegisteredKey
	json.NewDecoder(resp.Body).Decode(&keys)

	return keys, nil
}

func keysRegisterCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "register",
		Short: "Upload public key to portal",
		RunE:  runKeysRegister,
	}

	cmd.Flags().StringP("key", "k", "", "Path to public key (default: ~/.mosis/signing_key.pub)")
	cmd.Flags().StringP("name", "n", "", "Key name (e.g., 'MacBook Pro 2024')")

	return cmd
}

func runKeysRegister(cmd *cobra.Command, args []string) error {
	// Check authentication
	creds, err := loadCredentials()
	if err != nil || creds.AccessToken == "" {
		return fmt.Errorf("not logged in\n\nRun 'mosis login' first")
	}

	// Load public key
	keyPath, _ := cmd.Flags().GetString("key")
	if keyPath == "" {
		keyPath = filepath.Join(ConfigDir(), "signing_key.pub")
	}

	fmt.Printf("Reading public key from %s\n", keyPath)

	keyData, err := os.ReadFile(keyPath)
	if err != nil {
		return fmt.Errorf("public key not found: %s\n\nGenerate a key with: mosis keys generate", keyPath)
	}

	publicKey, err := mospkg.LoadPublicKey(keyData)
	if err != nil {
		return fmt.Errorf("invalid public key: %w", err)
	}

	fingerprint := mospkg.PublicKeyFingerprint(publicKey)
	fmt.Printf("Fingerprint: %s\n\n", fingerprint)

	// Get key name
	keyName, _ := cmd.Flags().GetString("name")
	if keyName == "" {
		fmt.Print("? Key name: ")
		reader := bufio.NewReader(os.Stdin)
		keyName, _ = reader.ReadString('\n')
		keyName = strings.TrimSpace(keyName)
	}

	if keyName == "" {
		keyName = "Default Key"
	}

	fmt.Println("\nUploading to portal...")

	// Register with portal
	portalURL := viper.GetString("portal_url")
	if err := registerKey(portalURL, creds.AccessToken, keyName, string(keyData)); err != nil {
		return fmt.Errorf("register key: %w", err)
	}

	fmt.Println("✓ Key registered successfully")
	fmt.Println(`
Your signing key is now active. Packages signed with this
key will be accepted for review.`)

	return nil
}

func registerKey(portalURL, token, name, publicKeyPEM string) error {
	url := fmt.Sprintf("%s/v1/signing-keys", portalURL)

	body := map[string]string{
		"name":       name,
		"public_key": publicKeyPEM,
	}
	jsonBody, _ := json.Marshal(body)

	req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBody))
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		// If portal not reachable, just succeed locally
		return nil
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("API error: %s", string(respBody))
	}

	return nil
}
