// Package cloudconfig baut die CloudConfigurationDetails.plist die
// während MobileBackup2-Restore ins iPhone-Filesystem injiziert wird.
//
// Schema verifiziert empirisch aus TechLockdown's extracted Manifest.db
// + live cloud-config dumps. Apple's DEP-fields-Set (ConfigurationSource=0
// + Org-metadata) ist der Bypass-Mechanismus für 14002-Validation auf
// already-supervised devices.
package cloudconfig

import (
	"bytes"
	"fmt"
	"os"
	"strings"

	"howett.net/plist"
)

// SkipSetupAll = TL's 31-key SkipSetup-Liste + "Intelligence" (iOS 18+ Apple-AI-Setup-Step
// den TL's altes binary noch nicht skipped). Empirisch verifiziert 2026-05-28: ohne
// Intelligence-Key zeigt iOS 26.x den Apple-Intelligence-Screen vor Kamera/SOS.
// Siehe memory: project_supervise_tl_parity_achieved.
var SkipSetupAll = []string{
	"Android", "Appearance", "AppleID", "AppStore", "Biometric",
	"Diagnostics", "DisplayTone", "FileVault", "HomeButtonSensitivity",
	"iCloudDiagnostics", "iCloudStorage", "iMessageAndFaceTime",
	"Intelligence", // iOS 18+ Apple-Intelligence-Setup
	"Location", "OnBoarding", "Passcode", "Payment", "Privacy",
	"Registration", "Restore", "RestoreCompleted", "UpdateCompleted",
	"ScreenTime", "ScreenSaver", "SIMSetup", "Siri", "SoftwareUpdate",
	"TapToSetup", "TOS", "WatchMigration", "Zoom", "Welcome",
}

// BuildOptions sind die runtime-vars für CloudConfigurationDetails.
type BuildOptions struct {
	OrganizationName  string   // "ReBreak"
	OrganizationEmail string   // "hello@rebreak.org"
	SupervisorCert    []byte   // DER-encoded cert from our identity
	SkipSetup         []string // default SkipSetupAll if nil
}

// Build encoded eine vollständige CloudConfigurationDetails.plist als
// **binary plist** bytes (matched TL's format — XML wäre auch valid aber
// TL nutzt binary).
//
// Field-Set verifiziert aus TL-extraction (bplist_01) + live-supervised iPhone.
func Build(opts BuildOptions) ([]byte, error) {
	if opts.OrganizationName == "" {
		opts.OrganizationName = "ReBreak"
	}
	if opts.OrganizationEmail == "" {
		opts.OrganizationEmail = "hello@rebreak.org"
	}
	if opts.SkipSetup == nil {
		opts.SkipSetup = SkipSetupAll
	}
	if profile := os.Getenv("REBREAK_CC_SKIPSETUP_PROFILE"); profile != "" {
		switch profile {
		case "camera_sos":
			// Kandidaten für die verbleibenden letzten Setup-Panels (Kamera/SOS/Intelligence).
			opts.SkipSetup = mergeSkipSetup(opts.SkipSetup,
				"CameraButton", "CameraControl", "EmergencySOS", "SOS",
				"Safety", "Intelligence", "AppleIntelligence", "AI")
		}
	}
	if extra := os.Getenv("REBREAK_CC_SKIPSETUP_EXTRA"); extra != "" {
		parts := strings.Split(extra, ",")
		clean := make([]string, 0, len(parts))
		for _, p := range parts {
			k := strings.TrimSpace(p)
			if k != "" {
				clean = append(clean, k)
			}
		}
		if len(clean) > 0 {
			opts.SkipSetup = mergeSkipSetup(opts.SkipSetup, clean...)
		}
	}

	configurationSource := int64(0)
	configurationWasApplied := true
	cloudConfigurationUIComplete := true
	postSetupProfileWasInstalled := true

	if v, ok := envBool("REBREAK_CC_CONFIGURATION_WAS_APPLIED"); ok {
		configurationWasApplied = v
	}
	if v, ok := envBool("REBREAK_CC_UI_COMPLETE"); ok {
		cloudConfigurationUIComplete = v
	}
	if v, ok := envBool("REBREAK_CC_POST_SETUP_PROFILE"); ok {
		postSetupProfileWasInstalled = v
	}
	if v, ok := envInt64("REBREAK_CC_CONFIGURATION_SOURCE"); ok {
		configurationSource = v
	}

	// AutoAdvanceSetup: 2026-05-28 09:25 empirisch verifiziert:
	//   true  → iPhone triggert iOS-Install-Screen (mini backup) + skipt
	//           Setup-Assistant → landet bei Apple Intelligence/Kamera.
	//           ABER: viele User-Settings (Sprache etc) gehen verloren.
	//   false → KEIN install-screen, iPhone bootet normal → Setup-Assistant
	//           zeigt Sprache/Region/Anrede + "teilweise eingerichtet"-Dialog.
	//
	// TL's extrahierte plist hat false ABER zeigt install-screen — d.h. TL
	// triggert das Verhalten anders (vermutlich via Restore-Options). Wir
	// behalten true bis wir den TL-Mechanismus für Settings-Preservation
	// gefunden haben (RestoreSystemFiles=false ist eine Kandidaten-Hypothese).
	autoAdvanceSetup := true
	if v, ok := envBool("REBREAK_CC_AUTO_ADVANCE"); ok {
		autoAdvanceSetup = v
	}

	// 2026-05-28 EMPIRISCH-VERIFIZIERT: TL's CloudConfigurationDetails enthält
	// KEIN `SupervisorHostCertificates`-Field (weder in Embed-Template noch in
	// Runtime-Output via MCInstall.GetCloudConfiguration). Wenn wir das Feld
	// SENDEN, partial-applied iOS auf fresh-activated devices: IsSupervised
	// bleibt false, andere Felder werden geschrieben. Ohne das Feld: full apply.
	//
	// Cert wird trotzdem in cert.LoadOrCreate() persistiert + ist für
	// lockdownd-pair-record relevant (separate channel zu cloud-config).
	// Wenn das Feld komplett weg ist, klappt fresh-supervise — re-supervise
	// auch (iPhone behält bestehenden cert oder ignoriert ihn).
	_ = opts.SupervisorCert // marked-unused — wir nutzen ihn nicht mehr in der plist

	// Wir bauen das dict in der Reihenfolge die TL nutzt (helps with iOS
	// validation falls Apple ordering-sensitive ist).
	cfg := map[string]interface{}{
		// Supervisor-Layer
		"IsSupervised":     true,
		"IsMDMUnremovable": int64(0), // matched TL's int format
		"IsMandatory":      false,
		"IsMultiUser":      false,
		"AllowPairing":     true,
		"OrganizationName": opts.OrganizationName,
		"OrganizationMagic": "", // leer — Apple's Sanity-check ist bei DEP-mode loose
		// SupervisorHostCertificates: bewusst NICHT mehr im Dict (siehe Block oben).
		"SkipSetup": toInterfaceSlice(opts.SkipSetup),
		// DEP-mode (the magic that bypasses 14002)
		"ConfigurationSource":          configurationSource,
		"ConfigurationURL":             "",
		"ConfigurationWasApplied":      configurationWasApplied,
		"CloudConfigurationUIComplete": cloudConfigurationUIComplete,
		"PostSetupProfileWasInstalled": postSetupProfileWasInstalled,
		"AutoAdvanceSetup":             autoAdvanceSetup,
		"AwaitDeviceConfigured":        false,
		// DEP-Org-Metadata (TL pattern — most "N/A" except email)
		"OrganizationAddress":      "N/A",
		"OrganizationAddressLine1": "N/A",
		"OrganizationAddressLine2": "N/A",
		"OrganizationCity":         "N/A",
		"OrganizationCountry":      "N/A",
		"OrganizationDepartment":   "N/A",
		"OrganizationEmail":        opts.OrganizationEmail,
		"OrganizationPhone":        "N/A",
		"OrganizationSupportPhone": "N/A",
		"OrganizationZipCode":      "N/A",
	}

	var buf bytes.Buffer
	enc := plist.NewEncoderForFormat(&buf, plist.BinaryFormat)
	if err := enc.Encode(cfg); err != nil {
		return nil, fmt.Errorf("cloudconfig: encode: %w", err)
	}
	return buf.Bytes(), nil
}

func envBool(name string) (bool, bool) {
	v, ok := os.LookupEnv(name)
	if !ok {
		return false, false
	}
	switch v {
	case "1", "true", "TRUE", "True":
		return true, true
	case "0", "false", "FALSE", "False":
		return false, true
	default:
		return false, false
	}
}

func envInt64(name string) (int64, bool) {
	v, ok := os.LookupEnv(name)
	if !ok {
		return 0, false
	}
	var parsed int64
	if _, err := fmt.Sscanf(v, "%d", &parsed); err != nil {
		return 0, false
	}
	return parsed, true
}

func toInterfaceSlice(ss []string) []interface{} {
	out := make([]interface{}, len(ss))
	for i, s := range ss {
		out[i] = s
	}
	return out
}

func mergeSkipSetup(base []string, extra ...string) []string {
	seen := make(map[string]struct{}, len(base)+len(extra))
	out := make([]string, 0, len(base)+len(extra))
	for _, k := range base {
		if _, ok := seen[k]; ok {
			continue
		}
		seen[k] = struct{}{}
		out = append(out, k)
	}
	for _, k := range extra {
		if _, ok := seen[k]; ok {
			continue
		}
		seen[k] = struct{}{}
		out = append(out, k)
	}
	return out
}
