package models

import (
	"encoding/json"
	"time"

	"gorm.io/gorm"

	"golang.org/x/crypto/bcrypt"
)

// User represents a user in the system
type User struct {
	ID              string          `json:"id" gorm:"primaryKey"`                  // Unique identifier
	Username        string          `json:"username" gorm:"uniqueIndex;not null"`  // Username
	Email           string          `json:"email" gorm:"uniqueIndex;not null"`     // User's email address
	Password        string          `json:"password,omitempty" gorm:"not null"`    // Password (hashed)
	Role            string          `json:"role" gorm:"default:'user'"`            // User role
	PreferencesJSON string          `json:"-" gorm:"column:preferences;type:text"` // User preferences stored as JSON string
	Preferences     UserPreferences `json:"preferences" gorm:"-"`                  // User preferences (transient field)
	CreatedAt       time.Time       `json:"createdAt" gorm:"autoCreateTime"`       // Creation timestamp
	UpdatedAt       time.Time       `json:"updatedAt" gorm:"autoUpdateTime"`       // Last update timestamp
	DeletedAt       gorm.DeletedAt  `json:"-" gorm:"index"`                        // Soft delete support
}

// UserPreferences represents user-specific settings
type UserPreferences struct {
	Theme           string      `json:"theme"`                     // User's theme preference
	Notifications   bool        `json:"notifications"`             // Notification preference
	DashboardLayout interface{} `json:"dashboardLayout,omitempty"` // Optional dashboard layout
}

// BeforeSave hook runs before saving a User - handles password hashing and preferences serialization
func (u *User) BeforeSave(tx *gorm.DB) error {
	// Only hash password if it's provided and not already hashed
	if u.Password != "" && len(u.Password) < 60 { // bcrypt hashes are typically 60 characters
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
		if err != nil {
			return err
		}
		u.Password = string(hashedPassword)
	}

	// Serialize preferences to JSON
	preferencesData, err := json.Marshal(u.Preferences)
	if err != nil {
		return err
	}
	u.PreferencesJSON = string(preferencesData)

	return nil
}

// AfterFind hook deserializes JSON preferences after fetching from database
func (u *User) AfterFind(tx *gorm.DB) error {
	if u.PreferencesJSON != "" {
		return json.Unmarshal([]byte(u.PreferencesJSON), &u.Preferences)
	}
	return nil
}

// CheckPassword verifies if the provided password matches the hashed one
func (u *User) CheckPassword(password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	return err == nil
}

type UserRole string

const (
	Admin     UserRole = "admin"
	Developer UserRole = "developer"
	Viewer    UserRole = "viewer"
)
