package models

import (
	"database/sql"
	"encoding/json"
	"strings"
	"time"
)

// ── Base ────────────────────────────────────

type BaseModel struct {
	ID        string    `json:"id" db:"id"`
	CreatedAt time.Time `json:"created_at" db:"created_at"`
	UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

// ── Scope Constants ─────────────────────────

const (
	ScopeGlobal   = "global"
	ScopeTeam     = "team"
	ScopePersonal = "personal"
)

// ── Team Role Constants ─────────────────────

const (
	TeamRoleAdmin  = "admin"
	TeamRoleMember = "member"
)

// ── Extension Tier Constants ────────────────

const (
	ExtTierBrowser  = "browser"
	ExtTierStarlark = "starlark"
	ExtTierSidecar  = "sidecar"
)

type User struct {
	BaseModel
	Username     string     `json:"username" db:"username"`
	Email        string     `json:"email" db:"email"`
	PasswordHash string     `json:"-" db:"password_hash"`
	DisplayName  string     `json:"display_name,omitempty" db:"display_name"`
	AvatarURL    string     `json:"avatar_url,omitempty" db:"avatar_url"`
	IsActive     bool       `json:"is_active" db:"is_active"`
	Settings     JSONMap    `json:"settings,omitempty" db:"settings"`
	LastLoginAt  *time.Time `json:"last_login_at,omitempty" db:"last_login_at"`
	AuthSource   string     `json:"auth_source" db:"auth_source"`
	ExternalID   *string    `json:"external_id,omitempty" db:"external_id"`
	Handle       string     `json:"handle" db:"handle"`
}

// =========================================
// TEAMS
// =========================================

type Team struct {
	BaseModel
	Name        string  `json:"name" db:"name"`
	Description string  `json:"description,omitempty" db:"description"`
	CreatedBy   string  `json:"created_by" db:"created_by"`
	IsActive    bool    `json:"is_active" db:"is_active"`
	Settings    JSONMap `json:"settings,omitempty" db:"settings"`
	MemberCount int     `json:"member_count,omitempty"` // computed
	MyRole      string  `json:"my_role,omitempty"`      // computed, user context
}

type TeamMember struct {
	ID       string `json:"id" db:"id"`
	TeamID   string `json:"team_id" db:"team_id"`
	UserID   string `json:"user_id" db:"user_id"`
	Role     string `json:"role" db:"role"`
	JoinedAt string `json:"joined_at" db:"joined_at"`
	// Joined fields from users table
	Email       string `json:"email,omitempty"`
	DisplayName string `json:"display_name,omitempty"`
	Username    string `json:"username,omitempty"`
}

// PLATFORM POLICIES

var PolicyDefaults = map[string]string{
	"allow_registration":  "true",
	"default_user_active": "false",
}

// HandleFromName generates a URL-safe @mention handle from a display name.
// "Veronica Sharpe" → "veronica-sharpe"
func HandleFromName(name string) string {
	h := strings.ToLower(strings.TrimSpace(name))
	h = strings.Map(func(r rune) rune {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			return r
		}
		if r == ' ' || r == '_' {
			return '-'
		}
		return -1
	}, h)
	// Collapse multiple hyphens
	for strings.Contains(h, "--") {
		h = strings.ReplaceAll(h, "--", "-")
	}
	h = strings.Trim(h, "-")
	if len(h) > 50 {
		h = h[:50]
	}
	return h
}

// =========================================
// AUDIT LOG
// =========================================

type AuditEntry struct {
	ID           string    `json:"id" db:"id"`
	ActorID      *string   `json:"actor_id,omitempty" db:"actor_id"`
	ActorName    *string   `json:"actor_name,omitempty" db:"-"` // resolved via JOIN, not a column
	Action       string    `json:"action" db:"action"`
	ResourceType string    `json:"resource_type" db:"resource_type"`
	ResourceID   string    `json:"resource_id,omitempty" db:"resource_id"`
	Metadata     JSONMap   `json:"metadata,omitempty" db:"metadata"`
	IPAddress    string    `json:"ip_address,omitempty" db:"ip_address"`
	UserAgent    string    `json:"user_agent,omitempty" db:"user_agent"`
	CreatedAt    time.Time `json:"created_at" db:"created_at"`
}

// =========================================
// JSON HELPERS
// =========================================

// JSONMap scans from/to JSONB columns.
type JSONMap map[string]interface{}

func (m *JSONMap) Scan(src interface{}) error {
	if src == nil {
		*m = nil
		return nil
	}
	var source []byte
	switch v := src.(type) {
	case []byte:
		source = v
	case string:
		source = []byte(v)
	default:
		return nil
	}
	result := make(JSONMap)
	if err := json.Unmarshal(source, &result); err != nil {
		return err
	}
	*m = result
	return nil
}

// Extension tier constants
type Extension struct {
	ID          string          `json:"id" db:"id"`
	ExtID       string          `json:"ext_id" db:"ext_id"`           // manifest id
	Name        string          `json:"name" db:"name"`
	Version     string          `json:"version" db:"version"`
	Tier        string          `json:"tier" db:"tier"`
	Description string          `json:"description" db:"description"`
	Author      string          `json:"author" db:"author"`
	Manifest    json.RawMessage `json:"manifest" db:"manifest"`
	IsSystem    bool            `json:"is_system" db:"is_system"`
	IsEnabled   bool            `json:"is_enabled" db:"is_enabled"`
	Scope       string          `json:"scope" db:"scope"`
	TeamID      *string         `json:"team_id,omitempty" db:"team_id"`
	InstalledBy *string         `json:"installed_by,omitempty" db:"installed_by"`
	CreatedAt   time.Time       `json:"created_at" db:"created_at"`
	UpdatedAt   time.Time       `json:"updated_at" db:"updated_at"`
}

// ExtensionUserSettings stores per-user overrides for an extension.
type ExtensionUserSettings struct {
	ExtensionID string          `json:"extension_id" db:"extension_id"`
	UserID      string          `json:"user_id" db:"user_id"`
	Settings    json.RawMessage `json:"settings" db:"settings"`
	IsEnabled   bool            `json:"is_enabled" db:"is_enabled"`
}

// UserExtension combines extension info with user-specific settings for API responses.
type UserExtension struct {
	Extension
	UserEnabled  *bool            `json:"user_enabled,omitempty"`
	UserSettings *json.RawMessage `json:"user_settings,omitempty"`
}

func NullString(s *string) sql.NullString {
	if s == nil {
		return sql.NullString{}
	}
	return sql.NullString{String: *s, Valid: true}
}

func NullFloat(f *float64) sql.NullFloat64 {
	if f == nil {
		return sql.NullFloat64{}
	}
	return sql.NullFloat64{Float64: *f, Valid: true}
}

func NullInt(i *int) sql.NullInt64 {
	if i == nil {
		return sql.NullInt64{}
	}
	return sql.NullInt64{Int64: int64(*i), Valid: true}
}

func StringPtr(s string) *string    { return &s }
func Float64Ptr(f float64) *float64 { return &f }
func IntPtr(i int) *int             { return &i }
func BoolPtr(b bool) *bool          { return &b }

// =========================================
// GROUPS (v0.16.0)
// =========================================

// Group scope constants (reuses ScopeGlobal, ScopeTeam from above)

// ResourceType constants for resource_grants
const (
	ResourceTypePersona       = "persona"
	ResourceTypeKnowledgeBase = "knowledge_base"
)

// GrantScope constants for resource_grants
const (
	GrantScopeTeamOnly = "team_only"
	GrantScopeGlobal   = "global"
	GrantScopeGroups   = "groups"
)

// Group is an access-control list. Decouples resource visibility from teams.
type Group struct {
	BaseModel
	Name               string   `json:"name" db:"name"`
	Description        string   `json:"description" db:"description"`
	Scope              string   `json:"scope" db:"scope"`      // global, team
	TeamID             *string  `json:"team_id,omitempty" db:"team_id"`
	CreatedBy          *string  `json:"created_by,omitempty" db:"created_by"`
	Source             string   `json:"source" db:"source"`    // manual (default), oidc, system
	Permissions []string `json:"permissions" db:"permissions"`
	MemberCount int      `json:"member_count,omitempty"` // computed, not a DB column
}

// GroupPatch holds optional fields for updating a group.
type GroupPatch struct {
	Name        *string   `json:"name,omitempty"`
	Description *string   `json:"description,omitempty"`
	Permissions *[]string `json:"permissions,omitempty"`
}

// GroupMember links a user to a group.
type GroupMember struct {
	ID      string    `json:"id" db:"id"`
	GroupID string    `json:"group_id" db:"group_id"`
	UserID  string    `json:"user_id" db:"user_id"`
	AddedBy string    `json:"added_by" db:"added_by"`
	AddedAt time.Time `json:"added_at" db:"added_at"`
	// Joined fields from users table (for list responses)
	Username    string `json:"username,omitempty"`
	Email       string `json:"email,omitempty"`
	DisplayName string `json:"display_name,omitempty"`
}

// ResourceGrant controls who can USE a resource (personas, KBs) via groups.
// One row per resource. Not to be confused with persona_grants (what a Persona can DO).
type ResourceGrant struct {
	BaseModel
	ResourceType  string   `json:"resource_type" db:"resource_type"`
	ResourceID    string   `json:"resource_id" db:"resource_id"`
	GrantScope    string   `json:"grant_scope" db:"grant_scope"`
	GrantedGroups []string `json:"granted_groups" db:"granted_groups"` // UUID array
	CreatedBy     string   `json:"created_by" db:"created_by"`
}

// KnowledgeBase is a named collection of documents with vector embeddings.
// PersonaKB binds a knowledge base to a persona.
// KBDocument is a single uploaded file within a knowledge base.
// KBChunk is a text segment from a document with its embedding vector.
// KBSearchResult is a single result from similarity search.
// ChannelKB represents a knowledge base linked to a channel.
// PROVIDER HEALTH (v0.22.0)

// ProviderStatus represents the derived health state.

// AvgLatencyMs returns the average latency, or 0 if no requests.

// ErrorRate returns the error fraction, or 0 if no requests.

// CAPABILITY OVERRIDES (v0.22.0)

// CapabilityOverride is an admin correction for a model's capabilities.
// ROUTING POLICIES (v0.22.2)

// RoutingPolicy is one routing rule that controls how requests are
// dispatched to provider configs. Stored in the routing_policies table.
