package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"

	"github.com/spf13/cobra"
)

func certCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "cert",
		Short: "Certificate management",
	}

	cmd.AddCommand(certRenewCommand())
	return cmd
}

func certRenewCommand() *cobra.Command {
	var installFlag string
	var caCertFlag string

	cmd := &cobra.Command{
		Use:   "renew <hostname>",
		Short: "Request a new certificate from Metacrypt",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			hostname := args[0]

			addr := os.Getenv("METACRYPT_ADDR")
			if addr == "" {
				addr = "https://metacrypt.svc.mcp.metacircular.net:8443"
			}
			token := os.Getenv("METACRYPT_TOKEN")
			if token == "" {
				return fmt.Errorf("METACRYPT_TOKEN environment variable is required")
			}

			// Build request body.
			reqBody := map[string]any{
				"mount":     "pki",
				"operation": "issue",
				"path":      "web",
				"data": map[string]any{
					"issuer":      "web",
					"common_name": hostname,
					"profile":     "server",
					"dns_names":   []string{hostname},
					"ttl":         "2160h",
				},
			}
			bodyBytes, err := json.Marshal(reqBody)
			if err != nil {
				return fmt.Errorf("marshal request: %w", err)
			}

			// Configure TLS.
			tlsCfg := &tls.Config{
				MinVersion: tls.VersionTLS13,
			}
			if caCertFlag != "" {
				caPEM, err := os.ReadFile(caCertFlag)
				if err != nil {
					return fmt.Errorf("read CA cert: %w", err)
				}
				pool := x509.NewCertPool()
				if !pool.AppendCertsFromPEM(caPEM) {
					return fmt.Errorf("failed to parse CA cert")
				}
				tlsCfg.RootCAs = pool
			} else {
				tlsCfg.InsecureSkipVerify = true
			}

			client := &http.Client{
				Transport: &http.Transport{
					TLSClientConfig: tlsCfg,
				},
			}

			url := addr + "/v1/engine/request"
			fmt.Printf(">>> POST %s\n", url)

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

			resp, err := client.Do(req)
			if err != nil {
				return fmt.Errorf("request failed: %w", err)
			}
			defer resp.Body.Close()

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

			if resp.StatusCode != http.StatusOK {
				return fmt.Errorf("metacrypt returned %d: %s", resp.StatusCode, string(respBody))
			}

			var result map[string]any
			if err := json.Unmarshal(respBody, &result); err != nil {
				return fmt.Errorf("parse response: %w", err)
			}

			certPEM, _ := result["cert_pem"].(string)
			chainPEM, _ := result["chain_pem"].(string)
			keyPEM, _ := result["key_pem"].(string)

			if certPEM == "" || keyPEM == "" {
				return fmt.Errorf("response missing cert_pem or key_pem")
			}

			// Combine leaf and chain into full cert.
			fullCert := certPEM
			if chainPEM != "" {
				fullCert = certPEM + "\n" + chainPEM
			}

			// Parse and display cert details.
			tlsCert, err := tls.X509KeyPair([]byte(fullCert), []byte(keyPEM))
			if err != nil {
				return fmt.Errorf("parse cert: %w", err)
			}
			leaf, err := x509.ParseCertificate(tlsCert.Certificate[0])
			if err != nil {
				return fmt.Errorf("parse leaf cert: %w", err)
			}

			fmt.Printf("Certificate issued:\n")
			fmt.Printf("  Serial:  %s\n", leaf.SerialNumber.Text(16))
			fmt.Printf("  CN:      %s\n", leaf.Subject.CommonName)
			fmt.Printf("  Expires: %s\n", leaf.NotAfter.Format("2006-01-02 15:04:05 UTC"))

			// Install to remote node if requested.
			if installFlag != "" {
				parts := strings.SplitN(installFlag, ":", 2)
				if len(parts) != 2 {
					return fmt.Errorf("--install must be <node>:<path> (e.g. mcp:/srv/mc-proxy/tls)")
				}
				node := parts[0]
				remotePath := parts[1]

				cfg, err := loadCfg()
				if err != nil {
					return err
				}

				nodeCfg, err := cfg.FindNode(node)
				if err != nil {
					return err
				}

				host := nodeCfg.Host
				if nodeCfg.User != "" {
					host = nodeCfg.User + "@" + nodeCfg.Host
				}

				// Write temp files and SCP them.
				certFile, err := os.CreateTemp("", "cert-*.pem")
				if err != nil {
					return fmt.Errorf("create temp cert: %w", err)
				}
				defer os.Remove(certFile.Name())

				keyFile, err := os.CreateTemp("", "key-*.pem")
				if err != nil {
					return fmt.Errorf("create temp key: %w", err)
				}
				defer os.Remove(keyFile.Name())

				if _, err := certFile.WriteString(fullCert); err != nil {
					return fmt.Errorf("write temp cert: %w", err)
				}
				certFile.Close()

				if _, err := keyFile.WriteString(keyPEM); err != nil {
					return fmt.Errorf("write temp key: %w", err)
				}
				keyFile.Close()

				certDest := host + ":" + remotePath + "/cert.pem"
				keyDest := host + ":" + remotePath + "/key.pem"

				if err := run("scp", certFile.Name(), certDest); err != nil {
					return fmt.Errorf("scp cert: %w", err)
				}
				if err := run("scp", keyFile.Name(), keyDest); err != nil {
					return fmt.Errorf("scp key: %w", err)
				}

				fmt.Printf("Installed cert and key to %s:%s/\n", node, remotePath)
			}

			return nil
		},
	}

	cmd.Flags().StringVar(&installFlag, "install", "", "install cert to <node>:<path>")
	cmd.Flags().StringVar(&caCertFlag, "ca-cert", "", "CA certificate for Metacrypt TLS verification")
	return cmd
}
