// SPDX-License-Identifier: GPL-3.0-or-later

// Package cli provides the restore-account command.
package cli

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
)

// RestoreAccountOptions contains options for the restore-account command.
type RestoreAccountOptions struct {
	KeyFile   string
	ConfigDir string
	Force     bool
}

// newRestoreAccountCommand creates the restore-account command.
func (c *CLI) newRestoreAccountCommand() *cobra.Command {
	opts := &RestoreAccountOptions{}

	cmd := &cobra.Command{
		Use:   "restore-account",
		Short: "Restore account from backup key file",
		Long: `Restore an existing account from a backup key file.

Use this command:
- After losing your device and setting up a new one
- To access your account on a second device
- To recover from a corrupted local configuration

The backup key file contains your certificate (identity) and the
encryption key for your data. Your backups remain on the server.`,
		Example: `  # Restore from USB drive
  privatebackup restore-account --key-file /media/usb/privatebackup-key.pem

  # Restore from downloaded backup
  privatebackup restore-account --key-file ~/Downloads/privatebackup-key.pem

  # Force replacement of existing account
  privatebackup restore-account --key-file /path/to/key.pem --force`,
		RunE: func(cmd *cobra.Command, args []string) error {
			return c.runRestoreAccount(opts)
		},
	}

	flags := cmd.Flags()
	flags.StringVar(&opts.KeyFile, "key-file", "", "Path to backup key file (required)")
	flags.StringVar(&opts.ConfigDir, "config-dir", "", "Config directory (default: ~/.config/privatebackup)")
	flags.BoolVar(&opts.Force, "force", false, "Skip confirmation when replacing existing account")

	_ = cmd.MarkFlagRequired("key-file")

	return cmd
}

// runRestoreAccount executes the restore-account command.
func (c *CLI) runRestoreAccount(opts *RestoreAccountOptions) error {
	c.renderer.Heading("Restoring Account from Backup Key")
	c.renderer.Newline()

	// Check if key file exists
	keyPath := expandPath(opts.KeyFile)
	if _, err := os.Stat(keyPath); os.IsNotExist(err) {
		return fmt.Errorf("key file not found: %s", keyPath)
	}

	c.renderer.Info("Reading backup key file...")

	// Read key file to validate it
	keyData, err := os.ReadFile(keyPath)
	if err != nil {
		return fmt.Errorf("failed to read key file: %w", err)
	}

	// Validate key file format (should be PEM)
	if !isPEMFile(keyData) {
		return fmt.Errorf("invalid key file format (expected PEM)")
	}

	c.renderer.Success("Valid backup key file")
	c.renderer.Newline()

	// Check if backend is running
	ctx, cancel := c.GetContext()
	defer cancel()

	_, err = c.client.Health(ctx)
	if err != nil {
		return fmt.Errorf("backend daemon is not running. Start it first with: privatebackup daemon start")
	}

	// Check if account already exists
	existingAccount, err := c.client.Account(ctx)
	if err == nil && existingAccount != nil && existingAccount.CertHash != "" {
		// Show warning about existing account
		if !opts.Force {
			c.renderer.Box(
				"⚠️  WARNING: ACCOUNT REPLACEMENT  ⚠️",
				fmt.Sprintf(`You already have a Private Archive account on this device.

Current Account:
  Cert Hash: %s
  Storage: %s

If you continue:
  • Current account will be REMOVED from this device
  • Current backup key file stops working here
  • You can still access current account with its key file`,
					truncateHash(existingAccount.CertHash),
					formatBytes(existingAccount.StorageUsedBytes)))
			c.renderer.Newline()

			confirmed, err := c.prompter.ConfirmDangerous(
				"To confirm account replacement:",
				"REPLACE",
			)
			if err != nil {
				return err
			}
			if !confirmed {
				c.renderer.Info("Account restoration cancelled.")
				return nil
			}

			c.renderer.Success("Current account configuration removed")
			c.renderer.Newline()
		}
	}

	// Check Tor connection
	c.renderer.Info("Connecting to Tor network...")
	torStatus, err := c.client.TorStatus(ctx)
	if err != nil {
		return fmt.Errorf("failed to check Tor status: %w", err)
	}

	if !torStatus.Connected {
		return fmt.Errorf("Tor is not connected. Check daemon status with: privatebackup daemon status")
	}
	c.renderer.Success("Connected to Tor network")
	c.renderer.Newline()

	// Restore account by loading key file into backend
	// The backend handles the actual key file import
	c.renderer.Info("Verifying account with server...")

	// Copy key file to config directory
	configDir := opts.ConfigDir
	if configDir == "" {
		configDir = c.configMgr.GetConfigDir()
	}
	destKeyPath := filepath.Join(configDir, "privatebackup-key.pem")

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

	// Copy key file
	if err := copyFile(keyPath, destKeyPath); err != nil {
		return fmt.Errorf("failed to copy key file: %w", err)
	}

	// Set restrictive permissions
	if err := os.Chmod(destKeyPath, 0600); err != nil {
		c.renderer.Warning("Failed to set key file permissions")
	}

	// Verify account exists on server by fetching account info
	account, err := c.client.Account(ctx)
	if err != nil {
		// If account doesn't exist, this is an error
		os.Remove(destKeyPath) // Clean up copied key
		return fmt.Errorf("account not found on server. The key file may be invalid or the account was deleted")
	}

	c.renderer.Success("Account exists on server")
	c.renderer.Newline()

	// Display account info
	c.renderer.Section("Account Status")
	c.renderer.KeyValue("Certificate Hash", truncateHash(account.CertHash))
	c.renderer.KeyValue("Storage Used", formatBytes(account.StorageUsedBytes))
	c.renderer.KeyValue("Credits", fmt.Sprintf("%.2f", account.CreditsRemaining))
	c.renderer.KeyValue("Status", account.Status)
	c.renderer.Newline()

	c.renderer.Info("Restoring local configuration...")
	c.renderer.Success("Account restored successfully!")
	c.renderer.Newline()

	c.renderer.Info("You can now run 'privatebackup restore' to retrieve your files.")

	return nil
}

// isPEMFile checks if data looks like a PEM file.
func isPEMFile(data []byte) bool {
	// Check for PEM header
	pemHeader := "-----BEGIN"
	if len(data) < len(pemHeader) {
		return false
	}
	for i := 0; i < len(data)-len(pemHeader); i++ {
		match := true
		for j := 0; j < len(pemHeader); j++ {
			if data[i+j] != pemHeader[j] {
				match = false
				break
			}
		}
		if match {
			return true
		}
	}
	return false
}

// copyFile copies a file from src to dst.
func copyFile(src, dst string) error {
	data, err := os.ReadFile(src)
	if err != nil {
		return err
	}
	return os.WriteFile(dst, data, 0600)
}

// formatBytes formats bytes into human-readable form.
func formatBytes(b int64) string {
	const unit = 1024
	if b < unit {
		return fmt.Sprintf("%d B", b)
	}
	div, exp := int64(unit), 0
	for n := b / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "KMGTPE"[exp])
}
