// Package models — M33 malware detection schema. Five tables backing
// /jabali-admin/security?tab=malware. See ADR-0072 + migration 000081.
package models

import (
	"encoding/json"
	"time"
)

// Scanner identifies which engine produced a quarantine row or event.
const (
	ScannerMaldet = "maldet"
	ScannerClamAV = "clamav"
	ScannerYARA   = "yara"
)

// MalwareEventType discriminates the kind of detection. Free-form string
// at the schema level (varchar(64)) so new event types do not require
// a migration.
const (
	EventTypeFileHit          = "file_hit"
	EventTypeProcessExecAlert = "process_exec_alert"
	EventTypeChmodSuspicious  = "chmod_suspicious"
	EventTypeSuspiciousSyscall = "suspicious_syscall"
	EventTypeScanCompleted    = "scan_completed"
)

// MalwareSeverity mirrors the ENUM in migration 000081 for malware_events.
const (
	MalwareSeverityInfo     = "info"
	MalwareSeverityWarn     = "warn"
	MalwareSeverityCritical = "critical"
)

// MalwareQuarantine is one quarantined file. Multiple rows per scan
// session — one per HIT. Restored rows keep the row but flip
// RestoredAt; deleted rows flip DeletedAt. Hard-purge happens via the
// retention timer (Step 10).
type MalwareQuarantine struct {
	ID             string     `gorm:"type:varchar(26);primaryKey"                 json:"id"`
	UserID         *string    `gorm:"type:varchar(26);index:idx_quarantine_user"  json:"user_id,omitempty"`
	OriginalPath   string     `gorm:"type:varchar(2048);not null"                 json:"original_path"`
	QuarantinePath string     `gorm:"type:varchar(2048);not null"                 json:"quarantine_path"`
	Signature      string     `gorm:"type:varchar(255);not null"                  json:"signature"`
	Scanner        string     `gorm:"type:enum('maldet','clamav','yara');not null" json:"scanner"`
	SHA256         string     `gorm:"column:sha256;type:char(64);not null;index:idx_quarantine_sha" json:"sha256"`
	SizeBytes      uint64     `gorm:"type:bigint unsigned;not null;default:0"     json:"size_bytes"`
	DetectedAt     time.Time  `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"        json:"detected_at"`
	RestoredAt     *time.Time `gorm:"type:timestamp"                              json:"restored_at,omitempty"`
	RestoredReason *string    `gorm:"type:varchar(500)"                           json:"restored_reason,omitempty"`
	RestoredBy     *string    `gorm:"type:varchar(26)"                            json:"restored_by,omitempty"`
	DeletedAt      *time.Time `gorm:"type:timestamp;index:idx_quarantine_active"  json:"deleted_at,omitempty"`
}

// TableName pins to the exact table from migration 000081.
func (MalwareQuarantine) TableName() string { return "malware_quarantine" }

// MalwareEvent is one detection event from any scanner. raw_json holds
// the per-source raw payload for forensics; everything else is
// projected for fast querying / filtering. Notified flips to true once
// the M14 dispatcher consumes the row.
type MalwareEvent struct {
	ID         string          `gorm:"type:varchar(26);primaryKey"               json:"id"`
	EventType  string          `gorm:"type:varchar(64);not null"                 json:"event_type"`
	Source     string          `gorm:"type:enum('maldet','clamav','yara');not null" json:"source"`
	Severity   string          `gorm:"type:enum('info','warn','critical');not null;default:'warn'" json:"severity"`
	UserID     *string         `gorm:"type:varchar(26);index:idx_events_user"    json:"user_id,omitempty"`
	TargetPath *string         `gorm:"type:varchar(2048)"                        json:"target_path,omitempty"`
	TargetPID  *uint32         `gorm:"column:target_pid;type:int unsigned"       json:"target_pid,omitempty"`
	Signature  *string         `gorm:"type:varchar(255)"                         json:"signature,omitempty"`
	RawJSON    json.RawMessage `gorm:"type:json;not null"                        json:"raw_json"`
	Notified   bool            `gorm:"type:tinyint(1);not null;default:0;index:idx_events_notified" json:"notified"`
	CreatedAt  time.Time       `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP;index:idx_events_created" json:"created_at"`
}

// TableName pins to the exact table.
func (MalwareEvent) TableName() string { return "malware_events" }

// YARACustomRule is one admin-uploaded YARA rule file living under
// /etc/jabali/yara/. Disabled rules are renamed to *.yar.disabled on
// disk; we mirror the bool here so list APIs do not need to readdir
// twice. SHA256 lets the UI flag drift if a file changes outside the
// panel.
type YARACustomRule struct {
	ID         string    `gorm:"type:varchar(26);primaryKey"                       json:"id"`
	Filename   string    `gorm:"type:varchar(255);not null;uniqueIndex:uk_yara_filename" json:"filename"`
	Enabled    bool      `gorm:"type:tinyint(1);not null;default:1"                json:"enabled"`
	UploadedBy string    `gorm:"type:varchar(26);not null"                         json:"uploaded_by"`
	SHA256     string    `gorm:"column:sha256;type:char(64);not null"              json:"sha256"`
	CreatedAt  time.Time `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
	UpdatedAt  time.Time `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
}

// TableName pins to the exact table.
func (YARACustomRule) TableName() string { return "yara_custom_rules" }

// MalwareSettings is the singleton row holding the malware stack's
// runtime knobs. CHECK (id = 1) in the schema enforces the singleton
// invariant; the repository EnsureDefault is a no-op when the row
// already exists (seeded by migration 000081).
type MalwareSettings struct {
	ID                   uint8  `gorm:"type:tinyint unsigned;primaryKey;default:1" json:"id"`
	ScheduleCron         string `gorm:"type:varchar(64);not null;default:'0 3 * * *'" json:"schedule_cron"`
	QuarantineRetainDays uint   `gorm:"type:int unsigned;not null;default:30"      json:"quarantine_retain_days"`
	MaxScanMB            uint   `gorm:"column:max_scan_mb;type:int unsigned;not null;default:256"     json:"max_scan_mb"`
	NotifyThreshold      string `gorm:"type:enum('info','warn','critical');not null;default:'warn'" json:"notify_threshold"`
	RealtimeEnabled      bool   `gorm:"type:tinyint(1);not null;default:0"         json:"realtime_enabled"`

	// M33.2 mail YARA scanner. Off by default — admin opts in via
	// Settings tab. ADR-0079.
	MailScanEnabled          bool   `gorm:"column:mail_scan_enabled;type:tinyint(1);not null;default:0"            json:"mail_scan_enabled"`
	MailScanAllFolders       bool   `gorm:"column:mail_scan_all_folders;type:tinyint(1);not null;default:0"        json:"mail_scan_all_folders"`
	MailScanSkipAddresses    string `gorm:"column:mail_scan_skip_addresses;type:text;not null;default:''"          json:"mail_scan_skip_addresses"`
	MailScanMaxAttachmentMB  uint   `gorm:"column:mail_scan_max_attachment_mb;type:int unsigned;not null;default:25" json:"mail_scan_max_attachment_mb"`
	MailScanTimeoutSec       uint   `gorm:"column:mail_scan_timeout_sec;type:int unsigned;not null;default:10"     json:"mail_scan_timeout_sec"`
	MailScanPerTickBudget    uint   `gorm:"column:mail_scan_per_tick_budget;type:int unsigned;not null;default:200" json:"mail_scan_per_tick_budget"`
}

// TableName pins to the exact table.
func (MalwareSettings) TableName() string { return "malware_settings" }

// MalwareUserScanState mirrors the ENUM in migration 000097.
const (
	MalwareUserScanStateRunning = "running"
	MalwareUserScanStateDone    = "done"
	MalwareUserScanStateFailed  = "failed"
)

// MalwareUserScan is the last-known scan-job row for a single Linux
// user. Upserted at scan start (state=running) and finalised by the
// post-scan-hook ingest path. One row per username. The Manual Scan UI
// joins this against the user list to surface "Last scanned" + "Status"
// columns and to disable re-scan while a job is still running.
type MalwareUserScan struct {
	Username    string     `gorm:"type:varchar(32);primaryKey"                       json:"username"`
	UserID      *string    `gorm:"type:varchar(26)"                                  json:"user_id,omitempty"`
	SessionID   string     `gorm:"type:varchar(26);not null;index:idx_malware_user_scans_session" json:"session_id"`
	TargetPath  string     `gorm:"type:varchar(255);not null;index:idx_malware_user_scans_path"   json:"target_path"`
	State       string     `gorm:"type:enum('running','done','failed');not null;default:'running';index:idx_malware_user_scans_state" json:"state"`
	Hits        uint       `gorm:"type:int unsigned;not null;default:0"              json:"hits"`
	ExitCode    *int       `gorm:"type:int"                                          json:"exit_code,omitempty"`
	StartedAt   time.Time  `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP" json:"started_at"`
	FinishedAt  *time.Time `gorm:"type:timestamp"                                    json:"finished_at,omitempty"`
}

// TableName pins to the exact table from migration 000097.
func (MalwareUserScan) TableName() string { return "malware_user_scans" }
