package vulnmodel

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"time"

	coremodel "security-center-backend/internal/model"
	"security-center-backend/internal/service/directory"
)

type SeverityLevel string

const (
	SeverityLow      SeverityLevel = "low"
	SeverityMedium   SeverityLevel = "medium"
	SeverityHigh     SeverityLevel = "high"
	SeverityCritical SeverityLevel = "critical"
)

type VulnerabilityStatus string

const (
	StatusActive        VulnerabilityStatus = "active"
	StatusRetesting     VulnerabilityStatus = "retesting"
	StatusFixed         VulnerabilityStatus = "fixed"
	StatusFalsePositive VulnerabilityStatus = "false_positive"
	StatusRiskAccepted  VulnerabilityStatus = "risk_accepted"
)

// StringArray stores a slice of strings using a JSON column.
type StringArray []string

func (a StringArray) Value() (driver.Value, error) {
	if len(a) == 0 {
		return "[]", nil
	}
	b, err := json.Marshal(a)
	if err != nil {
		return nil, err
	}
	return string(b), nil
}

func (a *StringArray) Scan(value interface{}) error {
	switch v := value.(type) {
	case []byte:
		if len(v) == 0 {
			*a = []string{}
			return nil
		}
		return json.Unmarshal(v, a)
	case string:
		if v == "" {
			*a = []string{}
			return nil
		}
		return json.Unmarshal([]byte(v), a)
	case nil:
		*a = []string{}
		return nil
	default:
		return fmt.Errorf("unsupported type for StringArray: %T", value)
	}
}

// UInt64Array stores a slice of uint64 values using a JSON column.
type UInt64Array []uint64

func (a UInt64Array) Value() (driver.Value, error) {
	if len(a) == 0 {
		return "[]", nil
	}
	b, err := json.Marshal(a)
	if err != nil {
		return nil, err
	}
	return string(b), nil
}

func (a *UInt64Array) Scan(value interface{}) error {
	switch v := value.(type) {
	case []byte:
		if len(v) == 0 {
			*a = []uint64{}
			return nil
		}
		return json.Unmarshal(v, a)
	case string:
		if v == "" {
			*a = []uint64{}
			return nil
		}
		return json.Unmarshal([]byte(v), a)
	case nil:
		*a = []uint64{}
		return nil
	default:
		return fmt.Errorf("unsupported type for UInt64Array: %T", value)
	}
}

type Vulnerability struct {
	coremodel.Base
	PublicID                    string                `gorm:"size:32;uniqueIndex" json:"public_id"`
	Title                       string                `gorm:"size:500" json:"title"`
	SeverityLevel               SeverityLevel         `gorm:"size:20" json:"severity_level"`
	Status                      VulnerabilityStatus   `gorm:"size:20" json:"status"`
	Application                 string                `gorm:"size:200" json:"application"`
	CodeRepository              string                `gorm:"size:512" json:"code_repository"`
	VulnerabilityType           string                `gorm:"size:120" json:"vulnerability_type"`
	DiscoveryChannel            string                `gorm:"size:20" json:"discovery_channel"`
	DiscoveryPhase              string                `gorm:"size:20" json:"discovery_phase"`
	NetworkEnv                  string                `gorm:"size:20" json:"network_env"`
	IsThirdParty                bool                  `json:"is_third_party"`
	InterfacePath               string                `gorm:"size:500" json:"interface_path"`
	Source                      string                `gorm:"size:120" json:"source"`
	AffectedAssetsCount         int                   `json:"affected_assets_count"`
	Tags                        StringArray           `gorm:"type:json" json:"tags"`
	HasPOCVerification          bool                  `json:"has_poc_verification"`
	DependencyName              string                `gorm:"size:200" json:"dependency_name"`
	DependencyVersion           string                `gorm:"size:100" json:"dependency_version"`
	BatchGroupID                *uint64               `json:"batch_group_id"`
	DiscovererEmployeeID        *uint64               `gorm:"index" json:"discoverer_employee_id,omitempty"`
	FixerEmployeeID             *uint64               `gorm:"index" json:"fixer_employee_id,omitempty"`
	SubmitterUserID             uint64                `gorm:"not null;index" json:"submitter_user_id"`
	OwnerEmployeeID             *uint64               `gorm:"index" json:"owner_employee_id,omitempty"`
	AuthorizedViewerEmployeeIDs UInt64Array           `gorm:"type:json" json:"authorized_viewer_employee_ids"`
	IsPublicViewable            bool                  `gorm:"not null;default:false;index" json:"is_public_viewable"`
	DepartmentID                *string               `gorm:"size:255;index" json:"department_id,omitempty"`
	Discoverer                  *directory.Employee   `gorm:"foreignKey:DiscovererEmployeeID" json:"discoverer,omitempty"`
	Fixer                       *directory.Employee   `gorm:"foreignKey:FixerEmployeeID" json:"fixer,omitempty"`
	Submitter                   *coremodel.User       `gorm:"foreignKey:SubmitterUserID" json:"submitter,omitempty"`
	Owner                       *directory.Employee   `gorm:"foreignKey:OwnerEmployeeID" json:"owner_employee,omitempty"`
	Department                  *directory.Department `gorm:"foreignKey:DepartmentID;references:LarkDepartmentID" json:"department,omitempty"`
	Description                 string                `gorm:"type:longtext" json:"description"`
	VerificationSteps           string                `gorm:"type:text" json:"verification_steps"`
	POCText                     string                `gorm:"type:text" json:"poc_text"`
	SubmittedAt                 time.Time             `json:"submitted_at"`
	ClaimedAt                   *time.Time            `json:"claimed_at"`
	FixRequestedAt              *time.Time            `json:"fix_requested_at"`
	FixedAt                     *time.Time            `json:"fixed_at"`
	ClosedAt                    *time.Time            `json:"closed_at"`
	SLADeadline                 time.Time             `json:"sla_deadline"`
	IsOverdue                   bool                  `json:"is_overdue"`
}
