package appstore

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/apex/log"
	"github.com/blacktop/go-plist"
	"github.com/blacktop/ipsw/internal/certs"
	"github.com/blacktop/ipsw/internal/utils"
	"github.com/fullsailor/pkcs7"
	"software.sslmate.com/src/go-pkcs12"
)

const rsaKeySize = 2048 // Standard key size

type ProvisionSigningFilesConfig struct {
	CertType string
	Platform string // "ios" (default), "macos", "tvos", "catalyst"
	BundleID string
	CSR      bool
	Email    string
	Country  string
	Install  bool
	KeepKey  bool
	Output   string
}

// ProvisionSigningFiles retrieves or creates, then installs, the necessary
// certificate and provisioning profile for iOS development signing.
func (as *AppStore) ProvisionSigningFiles(conf *ProvisionSigningFilesConfig) error {
	if as.token == "" {
		log.Info("Authenticating with App Store Connect API...")
		if err := as.createToken(defaultJWTLife); err != nil {
			return fmt.Errorf("failed to create API token: %w", err)
		}
	}

	as.conf = conf

	// Determine required certificate type and profile type based on input
	var requiredCertType CertificateType
	var requiredProfileType ProfileType
	var typeLabel string

	platform := strings.ToLower(conf.Platform)
	if platform == "" {
		platform = "ios"
	}

	switch strings.ToLower(conf.CertType) {
	case "development":
		requiredCertType = CT_DEVELOPMENT
		switch platform {
		case "macos":
			requiredProfileType = MAC_APP_DEVELOPMENT
		case "tvos":
			requiredProfileType = TVOS_APP_DEVELOPMENT
		case "catalyst":
			requiredProfileType = MAC_CATALYST_APP_DEVELOPMENT
		default:
			requiredProfileType = IOS_APP_DEVELOPMENT
		}
		typeLabel = "Development"
	case "adhoc":
		requiredCertType = CT_DISTRIBUTION
		switch platform {
		case "tvos":
			requiredProfileType = TVOS_APP_ADHOC
		default:
			requiredProfileType = IOS_APP_ADHOC
		}
		typeLabel = "AdHoc"
	case "distribution":
		requiredCertType = CT_DISTRIBUTION
		switch platform {
		case "macos":
			requiredProfileType = MAC_APP_STORE
		case "tvos":
			requiredProfileType = TVOS_APP_STORE
		case "catalyst":
			requiredProfileType = MAC_CATALYST_APP_STORE
		default:
			requiredProfileType = IOS_APP_STORE
		}
		typeLabel = "AppStore"
	default:
		return fmt.Errorf("invalid certificate type specified: %s", conf.CertType)
	}

	log.Infof("Checking for %s Certificate...", typeLabel)
	certResource, generatedKeyPath, err := as.ensureCertificate(requiredCertType, typeLabel, conf.Output, conf.CSR)
	if err != nil {
		return fmt.Errorf("failed to ensure %s certificate: %w", typeLabel, err)
	}

	certFilename := fmt.Sprintf("%s_%s.cer", strings.ToLower(typeLabel), certResource.ID)
	certPath := filepath.Join(conf.Output, certFilename)
	if err = os.WriteFile(certPath, certResource.Attributes.CertificateContent, 0644); err != nil {
		// Attempt cleanup if key was generated
		if generatedKeyPath != "" {
			os.Remove(generatedKeyPath)
		}
		return fmt.Errorf("failed saving %s certificate file %s: %w", typeLabel, certPath, err)
	}
	log.Infof("%s Certificate ID: %s (Saved to %s)", typeLabel, certResource.ID, certPath)
	// Bundle cert+key as P12 and optionally install the identity into Keychain.
	// This runs BEFORE the profile step so that even if profile creation fails,
	// the codesigning identity is available for manual use.
	if generatedKeyPath != "" {
		p12Filename := fmt.Sprintf("%s_%s.p12", strings.ToLower(typeLabel), certResource.ID)
		p12Path := filepath.Join(conf.Output, p12Filename)
		if err := bundlePKCS12(certPath, generatedKeyPath, p12Path, "ipsw"); err != nil {
			log.Warnf("Failed to bundle cert+key as P12: %v (cert and key saved separately)", err)
			log.Warnf("‼️  Private key saved to %s — secure or delete after manual import", generatedKeyPath)
		} else {
			log.Infof("Certificate + key bundled as %s", p12Path)
			if conf.Install {
				if err := installP12(p12Path, "ipsw"); err != nil {
					log.Warnf("Failed to install P12 into Keychain: %v", err)
					utils.Indent(log.Warn, 2)("Install manually: security import " + p12Path + " -k login.keychain-db -P ipsw -T /usr/bin/codesign")
					log.Warnf("‼️  Private key files retained at %s and %s (p12 password: \"ipsw\")", generatedKeyPath, p12Path)
				} else {
					log.Infof("Certificate + key identity installed into Keychain")
					if !conf.KeepKey {
						os.Remove(generatedKeyPath)
						os.Remove(p12Path)
						generatedKeyPath = "" // nothing on disk to reference
						log.Infof("Removed on-disk private key files (Keychain is canonical). Use --keep-key to retain.")
					} else {
						log.Warnf("‼️  Private key files retained at %s and %s (p12 password: \"ipsw\"). Back up offline then delete.",
							generatedKeyPath, p12Path)
					}
				}
				if err := ensureWWDRG3(); err != nil {
					log.Warnf("Failed to install Apple WWDR G3 intermediate: %v", err)
					utils.Indent(log.Warn, 2)("Download from https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer and import manually")
				}
			} else {
				log.Warnf("‼️  Private key files saved at %s and %s (p12 password: \"ipsw\"). Secure or delete after import.",
					generatedKeyPath, p12Path)
			}
		}
	} else if conf.Install {
		// No generated key — install cert only (user provided their own key)
		if err := InstallCertificateAndKey(certPath, ""); err != nil {
			log.Warnf("Failed to install certificate into Keychain: %v", err)
		} else {
			log.Infof("Certificate %s installed into login keychain.", certFilename)
		}
		if err := ensureWWDRG3(); err != nil {
			log.Warnf("Failed to install Apple WWDR G3 intermediate: %v", err)
			utils.Indent(log.Warn, 2)("Download from https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer and import manually")
		}
	}

	log.Infof("Checking for %s Provisioning Profile...", typeLabel)
	profileResource, err := as.ensureProvisioningProfile(conf.BundleID, certResource.ID, requiredProfileType, typeLabel, platform)
	if err != nil {
		if generatedKeyPath != "" {
			log.Warnf("Profile step failed but cert + key are saved at %s and %s; "+
				"retry with `ipsw appstore profile create`", certPath, generatedKeyPath)
		} else if conf.Install {
			log.Warnf("Profile step failed. Cert identity is in Keychain; "+
				"retry with `ipsw appstore profile create --bundle-id %s --certs %s`",
				conf.BundleID, certResource.ID)
		} else {
			log.Warnf("Profile step failed but cert is saved at %s; "+
				"retry with `ipsw appstore profile create`", certPath)
		}
		return fmt.Errorf("failed to ensure %s provisioning profile: %w", typeLabel, err)
	}

	profileFilename := fmt.Sprintf("ipsw_%s_%s.mobileprovision", typeLabel, conf.BundleID)
	profilePath := filepath.Join(conf.Output, profileFilename)
	if err = os.WriteFile(profilePath, profileResource.Attributes.ProfileContent, 0644); err != nil {
		return fmt.Errorf("failed saving %s provisioning profile file %s: %w", typeLabel, profilePath, err)
	}
	log.Infof("%s Provisioning Profile ID: %s (Saved to %s)", typeLabel, profileResource.ID, profilePath)

	if conf.Install {
		log.Info("Installing Provisioning Profile...")
		if installedName, err := InstallProvisioningProfile(profilePath); err != nil {
			log.Warnf("Failed to install provisioning profile: %v", err)
			utils.Indent(log.Warn, 2)("You may need to install it manually by copying it to ~/Library/MobileDevice/Provisioning Profiles/ (using the profile's UUID as filename.mobileprovision)")
		} else {
			log.Infof("Provisioning Profile installed: %s", installedName)
		}
	}

	return nil
}

// ensureCertificate finds a valid certificate of the required type or creates a new one.
// Takes the required CertificateType constant and a label for logging/filenames.
func (as *AppStore) ensureCertificate(requiredCertType CertificateType, typeLabel, outputDir string, newCSR bool) (certRes *Certificate, keyPath string, err error) {
	if !newCSR {
		log.Debugf("Fetching all certificates...")
		allCerts, err := as.GetCertificates()
		if err != nil {
			return nil, "", fmt.Errorf("getting certificates: %w", err)
		}
		log.Debugf("Retrieved %d certificates. Filtering for valid %s certs...", len(allCerts), typeLabel)
		for _, cert := range allCerts {
			// Check if it's the required type
			if cert.Attributes.CertificateType != requiredCertType {
				continue // Skip wrong types
			}
			// Check if the certificate is valid (not expired and more than 30 days left)
			if time.Time(cert.Attributes.ExpirationDate).After(time.Now().Add(30 * 24 * time.Hour)) {
				log.WithFields(log.Fields{
					"id":     cert.ID,
					"name":   cert.Attributes.Name,
					"serial": cert.Attributes.SerialNumber,
				}).Infof("Found valid existing %s", typeLabel)
				return &cert, "", nil // No key generated
			}
			log.WithFields(log.Fields{
				"id":      cert.ID,
				"name":    cert.Attributes.Name,
				"expires": cert.Attributes.ExpirationDate.Format(time.RFC1123),
			}).Debugf("Found %s expired/expiring soon", typeLabel)
		}
		log.Infof("No valid existing %s certificate found. Creating a new one...", typeLabel)
	} else {
		log.Infof("Forcing creation of a new %s certificate...", typeLabel)
	}

	log.Debug("Generating RSA private key...")
	privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
	if err != nil {
		return nil, "", fmt.Errorf("generating private key: %w", err)
	}
	privateKeyPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
	})
	// Save private key locally using typeLabel in filename
	keyFilename := fmt.Sprintf("%s_private_key.pem", strings.ToLower(typeLabel))
	generatedKeyPath := filepath.Join(outputDir, keyFilename)
	if err := os.WriteFile(generatedKeyPath, privateKeyPEM, 0600); err != nil {
		return nil, "", fmt.Errorf("saving generated private key: %w", err)
	}
	log.Debugf("Private key generated and saved to %s (Permissions 0600)", generatedKeyPath)

	log.Debug("Generating Certificate Signing Request (CSR)...")
	csrTemplate := x509.CertificateRequest{
		Subject: pkix.Name{
			ExtraNames: []pkix.AttributeTypeAndValue{
				{Type: certs.OIDEmailAddress, Value: as.conf.Email},
			},
			CommonName: fmt.Sprintf("%s Dev Key %s", typeLabel, time.Now().Format("20060102150405")),
			Country:    []string{as.conf.Country},
		},
		SignatureAlgorithm: x509.SHA256WithRSA,
		// EmailAddresses:     []string{as.conf.Email},
	}
	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, privateKey)
	if err != nil {
		os.Remove(generatedKeyPath) // Cleanup generated key
		return nil, "", fmt.Errorf("creating CSR: %w", err)
	}
	// csrPath := filepath.Join(outputDir, fmt.Sprintf("%s.certSigningRequest", strings.ToLower(typeLabel)))
	// if err := os.WriteFile(csrPath, csrBytes, 0600); err != nil {
	// 	return nil, "", fmt.Errorf("saving CSR: %w", err)
	// }
	// log.Infof("Certificate Signing Request (CSR) saved to %s", csrPath)
	// NOTE: openssl req -in development.certSigningRequest -noout -text
	log.Info("Submitting CSR to App Store Connect...")
	createdCert, err := as.CreateCertificate(
		string(requiredCertType), // Pass the required type as string
		base64.StdEncoding.EncodeToString(csrBytes),
	)
	if err != nil {
		os.Remove(generatedKeyPath) // Cleanup generated key
		return nil, "", fmt.Errorf("creating %s certificate via API: %w", typeLabel, err)
	}

	log.WithField("id", createdCert.ID).Infof("Successfully created new %s certificate", typeLabel)
	utils.Indent(log.Info, 2)(
		fmt.Sprintf("New Certificate Details - Name: %s, Serial: %s, Expires: %s",
			createdCert.Attributes.Name,
			createdCert.Attributes.SerialNumber,
			createdCert.Attributes.ExpirationDate.Format(time.RFC1123),
		))

	return createdCert, generatedKeyPath, nil
}

// ensureProvisioningProfile finds or creates a provisioning profile of the specified type.
// Takes the required ProfileType constant and a label for logging/filenames.
func (as *AppStore) ensureProvisioningProfile(bundleIDIdentifier string, certID string, requiredProfileType ProfileType, typeLabel, platform string) (prof *Profile, err error) {

	log.Debugf("Looking up Bundle ID resource for identifier: %s", bundleIDIdentifier)
	bundle, err := as.GetBundleIDByIdentifier(bundleIDIdentifier)
	if err != nil {
		return nil, fmt.Errorf("finding bundle ID '%s': %w", bundleIDIdentifier, err)
	}
	if bundle == nil {
		log.Infof("Bundle ID '%s' not found, registering...", bundleIDIdentifier)
		// ASC name field requires alphanumeric+spaces only; replace dots.
		displayName := strings.ReplaceAll(bundleIDIdentifier, ".", " ")
		resp, err := as.RegisterBundleID(displayName, bundleIDIdentifier)
		if err != nil {
			return nil, fmt.Errorf("registering bundle ID '%s': %w", bundleIDIdentifier, err)
		}
		bundle = &resp.Data
		log.Infof("Registered Bundle ID: %s (resource ID: %s)", bundleIDIdentifier, bundle.ID)
	}

	log.Debug("Listing all devices...")
	allDevices, err := as.GetDevices()
	if err != nil {
		return nil, fmt.Errorf("listing devices: %w", err)
	}

	deviceIDs := make([]string, 0)
	enabledDeviceCount := 0
	// Only include devices for Development and AdHoc profiles
	// AppStore profiles don't include specific devices
	if profileTypeNeedsDevices(requiredProfileType) {
		for _, dev := range allDevices {
			if dev.Attributes.Status == "ENABLED" { // Filter for enabled status
				deviceIDs = append(deviceIDs, dev.ID)
				enabledDeviceCount++
			}
		}
		if enabledDeviceCount == 0 {
			// Attempt to register the host Mac as a development device
			if udid, err := getHostProvisioningUDID(); err == nil {
				hostname, _ := os.Hostname()
				devPlatform := "MAC_OS"
				if platform == "ios" || platform == "tvos" {
					devPlatform = "IOS" // won't auto-register for iOS — user must register manually
					log.Warnf("No enabled devices found. Register your iOS device manually.")
				} else {
					log.Infof("No devices registered. Registering this Mac (%s)...", udid)
					dev, err := as.RegisterDevice(hostname, devPlatform, udid)
					if err != nil {
						log.Warnf("Device registration failed: %v (profile will be created without devices)", err)
					} else {
						deviceIDs = append(deviceIDs, dev.ID)
						enabledDeviceCount++
						log.Infof("Registered device: %s (%s)", hostname, udid)
					}
				}
			} else {
				log.Warnf("No enabled devices found and could not determine host UDID: %v", err)
			}
		}
		if enabledDeviceCount == 0 {
			log.Warnf("No enabled devices. %s profile will be created without devices.", typeLabel)
		} else {
			log.Infof("Found %d enabled devices to include in %s profile.", enabledDeviceCount, typeLabel)
		}
	} else {
		log.Debugf("%s profiles do not include specific devices.", typeLabel)
	}

	profileName := fmt.Sprintf("ipsw %s %s", typeLabel, bundleIDIdentifier)
	log.Infof("Attempting to find existing profile named: %s", profileName)

	allProfiles, err := as.GetProfiles()
	if err != nil {
		log.Warnf("Failed to list existing profiles, will attempt creation: %v", err)
	} else {
		log.Debugf("Retrieved %d profiles. Filtering for suitable %s profile...", len(allProfiles), typeLabel)
		for _, p := range allProfiles {
			// Check name, type, and state
			if p.Attributes.Name == profileName &&
				p.Attributes.ProfileType == requiredProfileType && // Check against the required type
				p.Attributes.ProfileState == "ACTIVE" {
				// Check if it contains the required certificate
				containsCert := false
				certs, err := as.GetProfileCerts(p.ID)
				if err != nil {
					log.Warnf("Failed to list certificates for profile %s: %v", p.ID, err)
					continue // Skip this profile
				}
				for _, c := range certs {
					if c.ID == certID {
						containsCert = true
						break
					}
				}
				// Check if it's for the correct bundle ID
				containsBundleID := false
				pb, err := as.GetProfileBundleID(p.ID)
				if err != nil {
					log.Warnf("Failed to list bundle IDs for profile %s: %v", p.ID, err)
					continue // Skip this profile
				}
				if pb.ID == bundle.ID {
					containsBundleID = true
				}
				// Check device count consistency (optional but good)
				// AdHoc/Dev profiles should ideally have the devices we found earlier
				// AppStore profiles should have zero devices associated
				devicesMatch := true
				if profileTypeNeedsDevices(requiredProfileType) {
					profileDevices, err := as.GetProfileDevices(p.ID)
					if err != nil {
						log.Warnf("Could not verify devices for existing profile %s: %v", p.ID, err)
						// Decide if this is critical - maybe continue if cert/bundle match
					} else if len(profileDevices) != len(deviceIDs) {
						log.Debugf("Existing profile %s has %d devices, but %d enabled devices found now. May need regeneration.", p.ID, len(profileDevices), len(deviceIDs))
						// Could force regeneration here if needed: continue
						devicesMatch = false // Mark as mismatch for now, maybe regenerate later
					}
				} else { // AppStore profile
					profileDevices, _ := as.GetProfileDevices(p.ID) // Check anyway
					if len(profileDevices) > 0 {
						log.Warnf("Existing AppStore profile %s unexpectedly contains devices.", p.ID)
						devicesMatch = false // AppStore profiles shouldn't have devices
					}
				}
				// If all conditions match, use this profile
				if containsCert && containsBundleID && devicesMatch { // Added devicesMatch check
					log.Infof("Found suitable existing %s profile: ID %s, State: %s", typeLabel, p.ID, p.Attributes.ProfileState)
					return &p, nil
				} else {
					log.Debugf("Profile %s matched name/type/state but failed checks: cert=%t, bundleID=%t, devices=%t", p.ID, containsCert, containsBundleID, devicesMatch)
				}
			}
		}
		log.Infof("No suitable active profile found named '%s'. Proceeding to create.", profileName)
		// Every profile with this name failed the find-suitable checks above
		// (wrong cert, INVALID, wrong bundle, etc). The name is taken Apple-side;
		// create will 409. Delete all name-matches regardless of state because
		// they are all stale/unsuitable.
		// (Apple's INVALID propagation is async after cert revoke, roughly 30s,
		// so checking IsInvalid() alone misses the immediate-rerun race window.)
		for _, p := range allProfiles {
			if p.Attributes.Name == profileName {
				log.Infof("Deleting stale profile %s (%s): name is taken and profile is unsuitable",
					p.ID, p.Attributes.ProfileState)
				if err := as.DeleteProfile(p.ID); err != nil {
					log.Warnf("Could not delete stale profile %s: %v (create may 409)", p.ID, err)
				}
			}
		}
	}

	log.Infof("Attempting to create a new %s Provisioning Profile...", typeLabel)
	// Use the requiredProfileType constant
	createResp, err := as.CreateProfile(
		profileName,
		string(requiredProfileType), // Pass the required type as string
		bundle.ID,
		[]string{certID},
		deviceIDs, // Pass the filtered list (empty for AppStore)
		false,     // offline=false
	)
	if err != nil {
		return nil, fmt.Errorf("creating %s provisioning profile: %w", typeLabel, err)
	}

	log.WithFields(log.Fields{
		"id":   createResp.Data.ID,
		"name": profileName,
	}).Infof("Successfully created %s Provisioning Profile", typeLabel)

	return &createResp.Data, nil
}

// InstallCertificateAndKey imports the certificate and optionally its private key into the login keychain.
func InstallCertificateAndKey(certPath string, keyPath string) error {
	// If keyPath is provided, install the private key
	if keyPath != "" {
		if err := installPrivateKey(keyPath); err != nil {
			return fmt.Errorf("installing private key: %w", err)
		}
	} else {
		log.Info("No private key path provided, skipping key import (assuming key already exists in keychain).")
	}

	// Install the certificate
	if err := installCertificate(certPath); err != nil {
		return fmt.Errorf("installing certificate: %w", err)
	}

	// Verify installation (same as before)
	return VerifyCertificateInstallation(certPath)
}

// installPrivateKey imports a private key into the login keychain.
func installPrivateKey(keyPath string) error {
	home, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("getting user home directory: %w", err)
	}

	// Determine correct keychain path (.keychain-db suffix is newer)
	keychainPath := filepath.Join(home, "Library", "Keychains", "login.keychain-db")
	if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
		keychainPath = filepath.Join(home, "Library", "Keychains", "login.keychain")
		if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
			return fmt.Errorf("cannot find login keychain at default locations")
		}
	}

	log.Infof("Importing private key %s into keychain %s", keyPath, keychainPath)
	// -P "" assumes empty passphrase. If key is encrypted, this will fail.
	// -T /usr/bin/codesign : Allow codesign to use this key without UI prompt.
	cmdKey := exec.Command("security", "import", keyPath, "-k", keychainPath, "-P", "", "-T", "/usr/bin/codesign")
	outputKey, errKey := cmdKey.CombinedOutput()
	if errKey != nil {
		// Retry without -P "" (for non-encrypted keys that might reject empty passphrase)
		log.Debugf("Initial key import failed, retrying without -P '': %v", errKey)
		cmdKeyRetry := exec.Command("security", "import", keyPath, "-k", keychainPath, "-T", "/usr/bin/codesign")
		outputKeyRetry, errKeyRetry := cmdKeyRetry.CombinedOutput()
		if errKeyRetry != nil {
			return fmt.Errorf("security import command failed for private key: %w\nAttempt 1 Output: %s\nAttempt 2 Output: %s", errKeyRetry, string(outputKey), string(outputKeyRetry))
		}
		log.Debugf("Security import (retry) output for key: %s", string(outputKeyRetry))
	} else {
		log.Debugf("Security import output for key: %s", string(outputKey))
	}
	log.Info("Private key imported.")
	return nil
}

// installCertificate imports a certificate into the login keychain.
func installCertificate(certPath string) error {
	home, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("getting user home directory: %w", err)
	}

	// Determine correct keychain path (.keychain-db suffix is newer)
	keychainPath := filepath.Join(home, "Library", "Keychains", "login.keychain-db")
	if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
		keychainPath = filepath.Join(home, "Library", "Keychains", "login.keychain")
		if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
			return fmt.Errorf("cannot find login keychain at default locations")
		}
	}

	// Import Certificate
	log.Infof("Importing certificate %s into keychain %s", certPath, keychainPath)
	cmdCert := exec.Command("security", "import", certPath, "-k", keychainPath)
	outputCert, errCert := cmdCert.CombinedOutput()
	if errCert != nil {
		return fmt.Errorf("security import command failed for certificate: %w\nOutput: %s", errCert, string(outputCert))
	}
	log.Debugf("Security import output for certificate: %s", string(outputCert))
	log.Info("Certificate imported.")
	return nil
}

// VerifyCertificateInstallation verifies that a certificate was properly installed.
func VerifyCertificateInstallation(certPath string) error {
	home, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("getting user home directory: %w", err)
	}

	// Determine correct keychain path (.keychain-db suffix is newer)
	keychainPath := filepath.Join(home, "Library", "Keychains", "login.keychain-db")
	if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
		keychainPath = filepath.Join(home, "Library", "Keychains", "login.keychain")
		if _, err := os.Stat(keychainPath); os.IsNotExist(err) {
			return fmt.Errorf("cannot find login keychain at default locations")
		}
	}

	// Verify Installation (Find Identity)
	// Need to parse the cert to get the Common Name for verification
	certBytes, err := os.ReadFile(certPath)
	if err != nil {
		log.Warnf("Cannot read certificate file %s for verification: %v", certPath, err)
		return nil // Don't fail the whole process, just warn
	}
	// Handle PEM encoding if necessary (App Store Connect usually provides DER)
	block, _ := pem.Decode(certBytes)
	var certRaw []byte
	if block != nil {
		certRaw = block.Bytes
	} else {
		certRaw = certBytes // Assume DER
	}

	parsedCert, err := x509.ParseCertificate(certRaw)
	if err != nil {
		log.Warnf("Cannot parse certificate %s for verification: %v", certPath, err)
		return nil // Don't fail, just warn
	}
	commonName := parsedCert.Subject.CommonName
	if commonName == "" {
		log.Warnf("Certificate %s has no Common Name, cannot verify identity precisely.", certPath)
		return nil
	}

	log.Infof("Verifying identity '%s' in keychain %s...", commonName, keychainPath)
	// -v : valid identities only, -p codesigning : for code signing usage
	cmdVerify := exec.Command("security", "find-identity", "-v", "-p", "codesigning", keychainPath)
	verifyOutput, errVerify := cmdVerify.CombinedOutput()

	found := false
	if errVerify == nil {
		for line := range strings.SplitSeq(string(verifyOutput), "\n") {
			// Match common name within quotes: "iPhone Developer: Your Name (TEAMID)"
			if strings.Contains(line, fmt.Sprintf(`"%s"`, commonName)) || strings.Contains(line, commonName) { // Be a bit lenient
				log.Infof("Verification successful: Found identity matching '%s'", commonName)
				log.Debugf("Full matching line: %s", strings.TrimSpace(line))
				found = true
				break
			}
		}
	}

	if !found {
		// This is a significant warning, but maybe not a fatal error depending on user workflow
		log.Warnf("Could not verify identity '%s' was successfully installed for code signing in %s.", commonName, keychainPath)
		if errVerify != nil {
			log.Debugf("Verification command 'security find-identity' failed: %v", errVerify)
		}
		return fmt.Errorf("failed to verify certificate and key combination in keychain for CN '%s'", commonName)
	}

	return nil
}

// InstallProvisioningProfile copies the profile to the standard location.
func InstallProvisioningProfile(profilePath string) (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("getting user home directory: %w", err)
	}

	profileDir := filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles")
	if err = os.MkdirAll(profileDir, 0755); err != nil {
		return "", fmt.Errorf("creating profiles directory %s: %w", profileDir, err)
	}

	profileData, err := os.ReadFile(profilePath)
	if err != nil {
		return "", fmt.Errorf("reading profile %s: %w", profilePath, err)
	}

	// Extract UUID from the profile
	destName := filepath.Base(profilePath)
	uuid, err := extractProfileUUID(profileData)
	if err != nil {
		log.Warnf("Could not extract UUID from profile: %v", err)
		log.Warnf("Using original filename instead: %s", destName)
	} else {
		// Default to original name if parsing fails
		destName = uuid + ".mobileprovision"
		log.Debugf("Extracted UUID from profile: %s", uuid)
	}

	destPath := filepath.Join(profileDir, destName)

	log.Infof("Copying profile %s to %s", profilePath, destPath)
	if err = os.WriteFile(destPath, profileData, 0644); err != nil {
		return "", fmt.Errorf("writing profile to %s: %w", destPath, err)
	}

	if err = os.Chmod(destPath, 0644); err != nil {
		log.Warnf("Failed to set permissions on %s: %v", destPath, err)
	}

	return destPath, nil
}

// extractProfileUUID parses a provisioning profile to extract its UUID.
// .mobileprovision files are in PKCS#7 (CMS) format with an embedded plist
func extractProfileUUID(profileData []byte) (string, error) {
	p7, err := pkcs7.Parse(profileData)
	if err != nil {
		return "", fmt.Errorf("parse PKCS#7 data: %w", err)
	}

	if len(p7.Content) == 0 {
		return "", fmt.Errorf("no content found in PKCS#7 data")
	}

	type minimalProfile struct {
		UUID string `plist:"UUID,omitempty"`
	}

	var profile minimalProfile
	if _, err := plist.Unmarshal(p7.Content, &profile); err != nil {
		return "", fmt.Errorf("unmarshal provisioning profile plist: %w", err)
	}

	if profile.UUID == "" {
		return "", fmt.Errorf("no UUID found in provisioning profile")
	}

	return profile.UUID, nil
}

// exportToP12 combines a certificate and private key into a PKCS#12 (.p12) file
// func exportToP12(certPath, keyPath, p12Path, password string) error {
// 	certData, err := os.ReadFile(certPath)
// 	if err != nil {
// 		return fmt.Errorf("reading certificate file: %w", err)
// 	}

// 	// Parse certificate - handle both PEM and DER formats
// 	var cert *x509.Certificate
// 	block, _ := pem.Decode(certData)
// 	if block != nil && block.Type == "CERTIFICATE" {
// 		// PEM format
// 		cert, err = x509.ParseCertificate(block.Bytes)
// 		if err != nil {
// 			return fmt.Errorf("parsing PEM certificate: %w", err)
// 		}
// 	} else {
// 		// DER format
// 		cert, err = x509.ParseCertificate(certData)
// 		if err != nil {
// 			// If DER parsing fails, try PEM decoding again just in case
// 			if block, _ := pem.Decode(certData); block != nil {
// 				cert, err = x509.ParseCertificate(block.Bytes)
// 				if err != nil {
// 					return fmt.Errorf("parsing certificate (PEM fallback): %w", err)
// 				}
// 			} else {
// 				return fmt.Errorf("parsing DER certificate: %w", err)
// 			}
// 		}
// 	}

// 	// Read private key file
// 	keyData, err := os.ReadFile(keyPath)
// 	if err != nil {
// 		return fmt.Errorf("reading private key file: %w", err)
// 	}

// 	// Parse private key - handle different PEM formats and DER
// 	var privateKey any
// 	block, _ = pem.Decode(keyData)
// 	if block != nil {
// 		// PEM format key
// 		switch block.Type {
// 		case "RSA PRIVATE KEY":
// 			privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
// 			if err != nil {
// 				return fmt.Errorf("parsing PKCS1 private key: %w", err)
// 			}
// 		case "PRIVATE KEY": // PKCS#8
// 			privateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
// 			if err != nil {
// 				return fmt.Errorf("parsing PKCS8 private key: %w", err)
// 			}
// 		// TODO: Add cases for other key types like EC PRIVATE KEY if needed
// 		default:
// 			return fmt.Errorf("unsupported PEM block type for private key: %s", block.Type)
// 		}
// 	} else {
// 		// Try to decode as PKCS#8 DER format first (more common)
// 		privateKey, err = x509.ParsePKCS8PrivateKey(keyData)
// 		if err != nil {
// 			// If PKCS#8 fails, try PKCS#1 DER format
// 			privateKey, err = x509.ParsePKCS1PrivateKey(keyData)
// 			if err != nil {
// 				return fmt.Errorf("parsing private key (DER PKCS8/PKCS1): %w", err)
// 			}
// 		}
// 	}

// 	// Ensure the key is usable by the Encode function
// 	switch pk := privateKey.(type) {
// 	case *rsa.PrivateKey:
// 		// Key is RSA, which is supported
// 	case *ecdsa.PrivateKey:
// 		// Key is ECDSA, which is supported
// 	default:
// 		return fmt.Errorf("unsupported private key type: %T", pk)
// 	}

// 	// Create PKCS#12 data using the SSLMate library's Encode function
// 	pfxData, err := pkcs12.Modern.Encode(privateKey, cert, nil, password)
// 	if err != nil {
// 		return fmt.Errorf("encoding PKCS#12 data: %w", err)
// 	}

// 	// Write the PKCS#12 data to file
// 	if err := os.WriteFile(p12Path, pfxData, 0600); err != nil {
// 		return fmt.Errorf("writing P12 file: %w", err)
// 	}

// 	log.Infof("Successfully exported certificate and key to %s", p12Path)
// 	return nil
// }

// bundlePKCS12 creates a PKCS#12 file from a DER certificate and PEM private key.
// Uses legacy 3DES/SHA1 encoding for macOS Keychain compatibility.
func bundlePKCS12(certPath, keyPath, p12Path, password string) error {
	certDER, err := os.ReadFile(certPath)
	if err != nil {
		return fmt.Errorf("reading certificate: %w", err)
	}
	cert, err := x509.ParseCertificate(certDER)
	if err != nil {
		return fmt.Errorf("parsing certificate: %w", err)
	}

	keyPEM, err := os.ReadFile(keyPath)
	if err != nil {
		return fmt.Errorf("reading private key: %w", err)
	}
	block, _ := pem.Decode(keyPEM)
	if block == nil {
		return fmt.Errorf("failed to decode PEM private key")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		// Try PKCS1 as fallback
		key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return fmt.Errorf("parsing private key: %w", err)
		}
	}

	p12Data, err := pkcs12.LegacyDES.Encode(key, cert, nil, password)
	if err != nil {
		return fmt.Errorf("encoding PKCS#12: %w", err)
	}

	if err := os.WriteFile(p12Path, p12Data, 0600); err != nil {
		return fmt.Errorf("writing P12 file: %w", err)
	}
	return nil
}

// installP12 imports a PKCS#12 file into the login keychain.
func installP12(p12Path, password string) error {
	keychainPath, err := loginKeychainPath()
	if err != nil {
		return err
	}
	log.Infof("Importing P12 %s into keychain %s", p12Path, keychainPath)
	cmd := exec.Command("security", "import", p12Path,
		"-k", keychainPath,
		"-P", password,
		"-T", "/usr/bin/codesign",
		"-T", "/usr/bin/security",
	)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("security import P12 failed: %w\nOutput: %s", err, string(out))
	}
	return nil
}

const wwdrG3URL = "https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer"

// ensureWWDRG3 downloads and imports the Apple WWDR G3 intermediate certificate
// into the login keychain. This is needed for the codesigning trust chain.
func ensureWWDRG3() error {
	keychainPath, err := loginKeychainPath()
	if err != nil {
		return err
	}

	resp, err := http.Get(wwdrG3URL)
	if err != nil {
		return fmt.Errorf("downloading WWDR G3: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("downloading WWDR G3: HTTP %s", resp.Status)
	}

	tmp, err := os.CreateTemp("", "wwdrg3-*.cer")
	if err != nil {
		return err
	}
	defer os.Remove(tmp.Name())

	if _, err := io.Copy(tmp, resp.Body); err != nil {
		tmp.Close()
		return fmt.Errorf("saving WWDR G3: %w", err)
	}
	tmp.Close()

	cmd := exec.Command("security", "import", tmp.Name(), "-k", keychainPath)
	out, err := cmd.CombinedOutput()
	if err != nil {
		// "already exists" is fine
		if strings.Contains(string(out), "already exists") {
			log.Debug("WWDR G3 intermediate already in keychain")
			return nil
		}
		return fmt.Errorf("importing WWDR G3: %w\nOutput: %s", err, string(out))
	}
	log.Info("Installed Apple WWDR G3 intermediate certificate")
	return nil
}

// loginKeychainPath returns the path to the user's login keychain.
func loginKeychainPath() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("getting user home directory: %w", err)
	}
	path := filepath.Join(home, "Library", "Keychains", "login.keychain-db")
	if _, err := os.Stat(path); os.IsNotExist(err) {
		path = filepath.Join(home, "Library", "Keychains", "login.keychain")
		if _, err := os.Stat(path); os.IsNotExist(err) {
			return "", fmt.Errorf("cannot find login keychain at default locations")
		}
	}
	return path, nil
}

// profileTypeNeedsDevices returns true for profile types that include device IDs.
func profileTypeNeedsDevices(pt ProfileType) bool {
	switch pt {
	case IOS_APP_DEVELOPMENT, IOS_APP_ADHOC,
		MAC_APP_DEVELOPMENT,
		TVOS_APP_DEVELOPMENT, TVOS_APP_ADHOC,
		MAC_CATALYST_APP_DEVELOPMENT:
		return true
	}
	return false
}

// getHostProvisioningUDID returns the Provisioning UDID of this Mac
// by parsing system_profiler output.
func getHostProvisioningUDID() (string, error) {
	out, err := exec.Command("system_profiler", "SPHardwareDataType").Output()
	if err != nil {
		return "", fmt.Errorf("running system_profiler: %w", err)
	}
	for line := range strings.SplitSeq(string(out), "\n") {
		line = strings.TrimSpace(line)
		if after, ok := strings.CutPrefix(line, "Provisioning UDID:"); ok {
			return strings.TrimSpace(after), nil
		}
	}
	return "", fmt.Errorf("Provisioning UDID not found in system_profiler output")
}
