package packs

import (
	"context"
	"crypto/rand"
	_ "embed"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	apperrors "github.com/nexuscrm/backend/pkg/errors"
	"github.com/nexuscrm/backend/pkg/utils"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

//go:embed profiles.json
var profilesJSON []byte

//go:embed bootstrap_identity.json
var bootstrapIdentityJSON []byte

// ---- Types ----

type bootstrapIdentityData struct {
	Users         []systemUserJSON        `json:"users"`
	ConnectedApps []bootstrapConnectedApp `json:"connected_apps"`
}

type systemUserJSON struct {
	ID        string  `json:"id,omitempty"`
	FirstName string  `json:"first_name"`
	LastName  string  `json:"last_name"`
	Email     string  `json:"email"`
	Username  string  `json:"username,omitempty"`
	Password  string  `json:"password"`
	ProfileID string  `json:"profile_id"`
	RoleID    *string `json:"role_id,omitempty"`
}

type bootstrapConnectedApp struct {
	ClientID string `json:"client_id"`
	Name     string `json:"name"`
	KeyID    string `json:"key_id"`
	Secret   string `json:"secret"`
}

// ---- Pack ----

// PlatformPolicyPack manages platform-owned security/identity metadata:
// profiles, bootstrap identity (admin user + required connected app),
// permissions, system permissions, and folder organization shares.
type PlatformPolicyPack struct{}

func (p *PlatformPolicyPack) Name() string      { return "platform-policy" }
func (p *PlatformPolicyPack) Category() Category { return PlatformPolicy }

func (p *PlatformPolicyPack) Apply(ctx context.Context, deps ApplyDeps) error {
	log.Println("📦 Applying platform-policy pack...")

	// Profiles (own the data + logic)
	if err := applySystemProfiles(ctx, deps); err != nil {
		return err
	}

	// Bootstrap identity: admin user + required connected app (own the data + logic)
	if err := provisionBootstrapIdentity(ctx, deps); err != nil {
		return err
	}

	// Permissions (owned by this pack — moved from bootstrap/)
	if err := InitializePermissions(deps.Permissions, deps.Metadata); err != nil {
		return fmt.Errorf("initialize permissions: %w", err)
	}
	if err := SeedSystemPermissions(ctx, deps.DB); err != nil {
		return fmt.Errorf("seed system permissions: %w", err)
	}
	// NOTE: ensureFolderOrganizationShares moved to PlatformConfig pack
	// (must run AFTER applyFolders creates the default public folders)

	log.Println("✅ platform-policy pack applied")
	return nil
}

func (p *PlatformPolicyPack) Verify(_ context.Context, _ VerifyDeps) error {
	return nil
}

// ---- Implementation ----

func applySystemProfiles(ctx context.Context, deps ApplyDeps) error {
	log.Println("🔧 Applying system profiles...")

	var profiles []models.Profile
	if err := json.Unmarshal(profilesJSON, &profiles); err != nil {
		return apperrors.NewInternalError("failed to parse profiles.json", err)
	}
	for i := range profiles {
		profiles[i].IsSystem = true
	}
	if err := deps.System.BatchUpsertProfiles(ctx, profiles); err != nil {
		return apperrors.NewInternalError("failed to batch upsert profiles", err)
	}
	log.Printf("   ✅ Ensure %d system profiles (batch)", len(profiles))

	// Rename SF-migrated profiles that collide with bootstrap names
	renames := []struct{ bootstrapID, currentName, newName string }{
		{constants.ProfileSystemAdmin, "System Administrator", "System Administrator (Salesforce)"},
		{constants.ProfileStandardUser, "Standard User", "Standard User (Salesforce)"},
	}
	for _, r := range renames {
		count, err := deps.System.RenameDuplicateProfilesByName(ctx, r.bootstrapID, r.currentName, r.newName)
		if err != nil {
			log.Printf("   ⚠️  Failed to rename duplicate profile %q: %v", r.currentName, err)
		} else if count > 0 {
			log.Printf("   ✅ Renamed %d duplicate %q → %q", count, r.currentName, r.newName)
		}
	}

	// Ensure PermissionSets for each profile
	if deps.Permissions != nil {
		for _, p := range profiles {
			if _, err := deps.Permissions.EnsureProfilePermissionSet(ctx, p.ID, p.Name); err != nil {
				log.Printf("   ⚠️  Failed to ensure PermissionSet for profile %s: %v", p.ID, err)
			}
		}
		log.Printf("   ✅ Ensured PermissionSets for %d profiles", len(profiles))
	}
	return nil
}

func provisionBootstrapIdentity(ctx context.Context, deps ApplyDeps) error {
	log.Println("🔧 Provisioning bootstrap identity...")

	var data bootstrapIdentityData
	if err := json.Unmarshal(bootstrapIdentityJSON, &data); err != nil {
		return apperrors.NewInternalError("failed to parse bootstrap_identity.json", err)
	}

	// Users
	type explicitPwd struct {
		email    string
		password string
	}
	var explicitPasswords []explicitPwd

	users := make([]models.User, len(data.Users))
	for i, u := range data.Users {
		id := u.ID
		if id == "" {
			id = utils.GenerateID()
		}
		username := u.Email
		if u.Username != "" {
			username = u.Username
		}
		password := u.Password
		if u.ProfileID == constants.ProfileSystemAdmin {
			pwd, explicit, err := resolveAdminPassword(u.Password, os.Getenv("NEXUS_ADMIN_PASSWORD"), os.Getenv("NEXUS_ENV"))
			if err != nil {
				return apperrors.NewInternalError("admin password resolution failed", err)
			}
			password = pwd
			if explicit {
				explicitPasswords = append(explicitPasswords, explicitPwd{email: u.Email, password: pwd})
			}
		}
		users[i] = models.User{
			ID: id, Username: username, Email: u.Email, Password: password,
			ProfileID: u.ProfileID, UserRoleID: u.RoleID,
			FirstName: u.FirstName, LastName: u.LastName, IsActive: true,
		}
	}
	if err := deps.System.BatchUpsertUsers(ctx, users); err != nil {
		return apperrors.NewInternalError("failed to batch upsert bootstrap users", err)
	}
	log.Printf("   ✅ Ensure %d bootstrap users (batch)", len(users))

	// Force-update admin password when explicitly configured via NEXUS_ADMIN_PASSWORD.
	// BatchUpsertUsers preserves existing bcrypt hashes (correct for restarts),
	// but explicit configuration must override old defaults like Admin123!.
	for _, ep := range explicitPasswords {
		if err := deps.System.ForceSetUserPassword(ctx, ep.email, ep.password); err != nil {
			return fmt.Errorf("force-update admin password: %w", err)
		}
	}

	// Required connected apps
	for _, app := range data.ConnectedApps {
		if err := deps.System.UpsertConnectedApp(ctx, app.ClientID, app.Name, app.KeyID, app.Secret); err != nil {
			log.Printf("   ⚠️  Failed to ensure connected app %s: %v", app.ClientID, err)
		} else {
			log.Printf("   ✅ Ensured connected app: %s", app.ClientID)
		}
	}
	return nil
}

func resolveAdminPassword(seedPassword, envPassword, nexusEnv string) (string, bool, error) {
	nexusEnv = strings.ToLower(strings.TrimSpace(nexusEnv))
	envPassword = strings.TrimSpace(envPassword)
	if envPassword != "" {
		return envPassword, true, nil
	}
	if nexusEnv == "production" || nexusEnv == "prod" || nexusEnv == "staging" {
		return "", false, fmt.Errorf("NEXUS_ADMIN_PASSWORD must be set (NEXUS_ENV=%q) — refusing to bootstrap without an explicit admin password", nexusEnv)
	}
	// Dev/CI/test: generate a random password so bootstrap doesn't break
	b := make([]byte, 12)
	if _, err := rand.Read(b); err != nil {
		return "", false, fmt.Errorf("failed to generate random admin password: %w", err)
	}
	generated := hex.EncodeToString(b)
	log.Printf("⚠️  NEXUS_ADMIN_PASSWORD not set — generated random admin password for non-production environment (NEXUS_ENV=%q). Password: %s", nexusEnv, generated)
	log.Printf("   Set NEXUS_ADMIN_PASSWORD for a stable password. Note: existing accounts keep their current password.")
	return generated, false, nil
}
