package localdb

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"time"
)

// AppSetting stores application settings in local SQLite
type AppSetting struct {
	Key       string    `gorm:"primaryKey" json:"key"`
	Value     string    `gorm:"not null" json:"value"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (AppSetting) TableName() string {
	return "app_settings"
}

// LocalConfigItem represents an item in a configuration
type LocalConfigItem struct {
	LotName   string  `json:"lot_name"`
	Quantity  int     `json:"quantity"`
	UnitPrice float64 `json:"unit_price"`
}

// LocalConfigItems is a slice of LocalConfigItem that can be stored as JSON
type LocalConfigItems []LocalConfigItem

func (c LocalConfigItems) Value() (driver.Value, error) {
	return json.Marshal(c)
}

func (c *LocalConfigItems) Scan(value interface{}) error {
	if value == nil {
		*c = make(LocalConfigItems, 0)
		return nil
	}
	var bytes []byte
	switch v := value.(type) {
	case []byte:
		bytes = v
	case string:
		bytes = []byte(v)
	default:
		return errors.New("type assertion failed for LocalConfigItems")
	}
	return json.Unmarshal(bytes, c)
}

func (c LocalConfigItems) Total() float64 {
	var total float64
	for _, item := range c {
		total += item.UnitPrice * float64(item.Quantity)
	}
	return total
}

// LocalStringList is a JSON-encoded list of strings stored as TEXT in SQLite.
type LocalStringList []string

func (s LocalStringList) Value() (driver.Value, error) {
	return json.Marshal(s)
}

func (s *LocalStringList) Scan(value interface{}) error {
	if value == nil {
		*s = make(LocalStringList, 0)
		return nil
	}
	var bytes []byte
	switch v := value.(type) {
	case []byte:
		bytes = v
	case string:
		bytes = []byte(v)
	default:
		return errors.New("type assertion failed for LocalStringList")
	}
	return json.Unmarshal(bytes, s)
}

// LocalConfiguration stores configurations in local SQLite
type LocalConfiguration struct {
	ID                    uint                        `gorm:"primaryKey;autoIncrement" json:"id"`
	UUID                  string                      `gorm:"uniqueIndex;not null" json:"uuid"`
	ServerID              *uint                       `json:"server_id"` // ID on MariaDB server, NULL if local only
	ProjectUUID           *string                     `gorm:"index" json:"project_uuid,omitempty"`
	CurrentVersionID      *string                     `gorm:"index" json:"current_version_id,omitempty"`
	IsActive              bool                        `gorm:"default:true;index" json:"is_active"`
	Name                  string                      `gorm:"not null" json:"name"`
	Items                 LocalConfigItems            `gorm:"type:text" json:"items"` // JSON stored as text in SQLite
	TotalPrice            *float64                    `json:"total_price"`
	CustomPrice           *float64                    `json:"custom_price"`
	Notes                 string                      `json:"notes"`
	IsTemplate            bool                        `gorm:"default:false" json:"is_template"`
	ServerCount           int                         `gorm:"default:1" json:"server_count"`
	ServerModel           string                      `gorm:"size:100" json:"server_model,omitempty"`
	SupportCode           string                      `gorm:"size:20" json:"support_code,omitempty"`
	Article               string                      `gorm:"size:80" json:"article,omitempty"`
	PricelistID           *uint                       `gorm:"index" json:"pricelist_id,omitempty"`
	WarehousePricelistID  *uint                       `gorm:"index" json:"warehouse_pricelist_id,omitempty"`
	CompetitorPricelistID *uint                       `gorm:"index" json:"competitor_pricelist_id,omitempty"`
	DisablePriceRefresh   bool                        `gorm:"default:false" json:"disable_price_refresh"`
	OnlyInStock           bool                        `gorm:"default:false" json:"only_in_stock"`
	VendorSpec            VendorSpec                  `gorm:"type:text" json:"vendor_spec,omitempty"`
	Line                  int                         `gorm:"column:line_no;index" json:"line"`
	PriceUpdatedAt        *time.Time                  `gorm:"type:timestamp" json:"price_updated_at,omitempty"`
	CreatedAt             time.Time                   `json:"created_at"`
	UpdatedAt             time.Time                   `json:"updated_at"`
	SyncedAt              *time.Time                  `json:"synced_at"`
	ConfigType            string                      `gorm:"default:server" json:"config_type"` // "server" | "storage"
	SyncStatus            string                      `gorm:"default:'local'" json:"sync_status"` // 'local', 'synced', 'modified'
	OriginalUserID        uint                        `json:"original_user_id"`                   // UserID from MariaDB for reference
	OriginalUsername      string                      `gorm:"not null;default:'';index" json:"original_username"`
	CurrentVersion        *LocalConfigurationVersion  `gorm:"foreignKey:CurrentVersionID;references:ID" json:"current_version,omitempty"`
	Versions              []LocalConfigurationVersion `gorm:"foreignKey:ConfigurationUUID;references:UUID" json:"versions,omitempty"`
}

func (LocalConfiguration) TableName() string {
	return "local_configurations"
}

type LocalProject struct {
	ID            uint       `gorm:"primaryKey;autoIncrement" json:"id"`
	UUID          string     `gorm:"uniqueIndex;not null" json:"uuid"`
	ServerID      *uint      `json:"server_id,omitempty"`
	OwnerUsername string     `gorm:"not null;index" json:"owner_username"`
	Code          string     `gorm:"not null;index:idx_local_projects_code_variant,priority:1" json:"code"`
	Variant       string     `gorm:"default:'';index:idx_local_projects_code_variant,priority:2" json:"variant"`
	Name          *string    `json:"name,omitempty"`
	TrackerURL    string     `json:"tracker_url"`
	IsActive      bool       `gorm:"default:true;index" json:"is_active"`
	IsSystem      bool       `gorm:"default:false;index" json:"is_system"`
	CreatedAt     time.Time  `json:"created_at"`
	UpdatedAt     time.Time  `json:"updated_at"`
	SyncedAt      *time.Time `json:"synced_at,omitempty"`
	SyncStatus    string     `gorm:"default:'local'" json:"sync_status"` // local/synced/pending
}

func (LocalProject) TableName() string {
	return "local_projects"
}

// LocalConfigurationVersion stores immutable full snapshots for each configuration version
type LocalConfigurationVersion struct {
	ID                string              `gorm:"primaryKey" json:"id"`
	ConfigurationUUID string              `gorm:"not null;index:idx_lcv_config_created,priority:1;index:idx_lcv_config_version,priority:1;uniqueIndex:idx_lcv_config_version_unique,priority:1" json:"configuration_uuid"`
	VersionNo         int                 `gorm:"not null;index:idx_lcv_config_version,sort:desc,priority:2;uniqueIndex:idx_lcv_config_version_unique,priority:2" json:"version_no"`
	Data              string              `gorm:"type:text;not null" json:"data"`
	ChangeNote        *string             `json:"change_note,omitempty"`
	CreatedBy         *string             `json:"created_by,omitempty"`
	AppVersion        string              `gorm:"size:64" json:"app_version,omitempty"`
	CreatedAt         time.Time           `gorm:"not null;autoCreateTime;index:idx_lcv_config_created,sort:desc,priority:2" json:"created_at"`
	Configuration     *LocalConfiguration `gorm:"foreignKey:ConfigurationUUID;references:UUID" json:"configuration,omitempty"`
}

func (LocalConfigurationVersion) TableName() string {
	return "local_configuration_versions"
}

// LocalPricelist stores cached pricelists from server
type LocalPricelist struct {
	ID        uint      `gorm:"primaryKey;autoIncrement" json:"id"`
	ServerID  uint      `gorm:"not null;uniqueIndex" json:"server_id"` // ID on MariaDB server
	Source    string    `gorm:"not null;default:'estimate';index:idx_local_pricelists_source_created_at,priority:1" json:"source"`
	Version   string    `gorm:"not null;index" json:"version"`
	Name      string    `json:"name"`
	CreatedAt time.Time `gorm:"index:idx_local_pricelists_source_created_at,priority:2,sort:desc" json:"created_at"`
	SyncedAt  time.Time `json:"synced_at"`
	IsUsed    bool      `gorm:"default:false" json:"is_used"` // Used by any local configuration
}

func (LocalPricelist) TableName() string {
	return "local_pricelists"
}

// LocalPricelistItem stores pricelist items
type LocalPricelistItem struct {
	ID           uint            `gorm:"primaryKey;autoIncrement" json:"id"`
	PricelistID  uint            `gorm:"not null;index" json:"pricelist_id"`
	LotName      string          `gorm:"not null" json:"lot_name"`
	LotCategory  string          `gorm:"column:lot_category" json:"lot_category,omitempty"`
	Price        float64         `gorm:"not null" json:"price"`
	AvailableQty *float64        `json:"available_qty,omitempty"`
	Partnumbers  LocalStringList `gorm:"type:text" json:"partnumbers,omitempty"`
}

func (LocalPricelistItem) TableName() string {
	return "local_pricelist_items"
}

// LocalComponent stores cached components for offline search (metadata only)
// All pricing is now sourced from local_pricelist_items based on configuration pricelist selection
type LocalComponent struct {
	LotName        string `gorm:"primaryKey" json:"lot_name"`
	LotDescription string `json:"lot_description"`
	Category       string `json:"category"`
	Model          string `json:"model"`
}

func (LocalComponent) TableName() string {
	return "local_components"
}

// LocalSyncGuardState stores latest sync readiness decision for UI and preflight checks.
type LocalSyncGuardState struct {
	ID                    uint       `gorm:"primaryKey;autoIncrement" json:"id"`
	Status                string     `gorm:"size:32;not null;index" json:"status"` // ready|blocked|unknown
	ReasonCode            string     `gorm:"size:128" json:"reason_code,omitempty"`
	ReasonText            string     `gorm:"type:text" json:"reason_text,omitempty"`
	RequiredMinAppVersion *string    `gorm:"size:64" json:"required_min_app_version,omitempty"`
	LastCheckedAt         *time.Time `json:"last_checked_at,omitempty"`
	UpdatedAt             time.Time  `json:"updated_at"`
}

func (LocalSyncGuardState) TableName() string {
	return "local_sync_guard_state"
}

// PendingChange stores changes that need to be synced to the server
type PendingChange struct {
	ID         int64     `gorm:"primaryKey;autoIncrement" json:"id"`
	EntityType string    `gorm:"not null;index" json:"entity_type"` // "configuration", "project", "specification"
	EntityUUID string    `gorm:"not null;index" json:"entity_uuid"`
	Operation  string    `gorm:"not null" json:"operation"` // "create", "update", "rollback", "deactivate", "reactivate", "delete"
	Payload    string    `gorm:"type:text" json:"payload"`  // JSON snapshot of the entity
	CreatedAt  time.Time `gorm:"not null" json:"created_at"`
	Attempts   int       `gorm:"default:0" json:"attempts"` // Retry count for sync
	LastError  string    `gorm:"type:text" json:"last_error,omitempty"`
}

func (PendingChange) TableName() string {
	return "pending_changes"
}

// LocalPartnumberBook stores a version snapshot of the PN→LOT mapping book (pull-only from PriceForge)
type LocalPartnumberBook struct {
	ID              uint            `gorm:"primaryKey;autoIncrement" json:"id"`
	ServerID        int             `gorm:"uniqueIndex;not null" json:"server_id"`
	Version         string          `gorm:"not null" json:"version"`
	CreatedAt       time.Time       `gorm:"not null" json:"created_at"`
	IsActive        bool            `gorm:"not null;default:true" json:"is_active"`
	PartnumbersJSON LocalStringList `gorm:"column:partnumbers_json;type:text" json:"partnumbers_json"`
}

func (LocalPartnumberBook) TableName() string {
	return "local_partnumber_books"
}

type LocalPartnumberBookLot struct {
	LotName string  `json:"lot_name"`
	Qty     float64 `json:"qty"`
}

type LocalPartnumberBookLots []LocalPartnumberBookLot

func (l LocalPartnumberBookLots) Value() (driver.Value, error) {
	return json.Marshal(l)
}

func (l *LocalPartnumberBookLots) Scan(value interface{}) error {
	if value == nil {
		*l = make(LocalPartnumberBookLots, 0)
		return nil
	}
	var bytes []byte
	switch v := value.(type) {
	case []byte:
		bytes = v
	case string:
		bytes = []byte(v)
	default:
		return errors.New("type assertion failed for LocalPartnumberBookLots")
	}
	return json.Unmarshal(bytes, l)
}

// LocalPartnumberBookItem stores the canonical PN composition pulled from PriceForge.
type LocalPartnumberBookItem struct {
	ID          uint                    `gorm:"primaryKey;autoIncrement" json:"id"`
	Partnumber  string                  `gorm:"not null" json:"partnumber"`
	LotsJSON    LocalPartnumberBookLots `gorm:"column:lots_json;type:text" json:"lots_json"`
	Description string                  `json:"description,omitempty"`
}

func (LocalPartnumberBookItem) TableName() string {
	return "local_partnumber_book_items"
}

// VendorSpecItem represents a single row in a vendor BOM specification
type VendorSpecItem struct {
	SortOrder           int                       `json:"sort_order"`
	VendorPartnumber    string                    `json:"vendor_partnumber"`
	Quantity            int                       `json:"quantity"`
	Description         string                    `json:"description,omitempty"`
	UnitPrice           *float64                  `json:"unit_price,omitempty"`
	TotalPrice          *float64                  `json:"total_price,omitempty"`
	ResolvedLotName     string                    `json:"resolved_lot_name,omitempty"`
	ResolutionSource    string                    `json:"resolution_source,omitempty"` // "book", "manual", "unresolved"
	ManualLotSuggestion string                    `json:"manual_lot_suggestion,omitempty"`
	LotQtyPerPN         int                       `json:"lot_qty_per_pn,omitempty"`
	LotAllocations      []VendorSpecLotAllocation `json:"lot_allocations,omitempty"`
	LotMappings         []VendorSpecLotMapping    `json:"lot_mappings,omitempty"`
}

type VendorSpecLotAllocation struct {
	LotName  string `json:"lot_name"`
	Quantity int    `json:"quantity"` // quantity of LOT per 1 vendor PN
}

// VendorSpecLotMapping is the canonical persisted LOT mapping for a vendor PN row.
// It stores all mapped LOTs (base + bundle) uniformly.
type VendorSpecLotMapping struct {
	LotName       string `json:"lot_name"`
	QuantityPerPN int    `json:"quantity_per_pn"`
}

// SyncLogEntry records the outcome of a single sync operation for diagnostics.
type SyncLogEntry struct {
	ID          uint      `gorm:"primaryKey;autoIncrement" json:"id"`
	SyncType    string    `gorm:"not null;index;size:32" json:"sync_type"`   // components | pricelists | push | full
	Status      string    `gorm:"not null;size:16" json:"status"`            // ok | error | skipped
	ErrorText   string    `gorm:"size:1000" json:"error_text,omitempty"`
	SyncedCount int       `gorm:"default:0" json:"synced_count"`
	StartedAt   time.Time `gorm:"not null;index" json:"started_at"`
	DurationMs  int64     `gorm:"default:0" json:"duration_ms"`
}

func (SyncLogEntry) TableName() string { return "sync_log" }

// VendorSpec is a JSON-encodable slice of VendorSpecItem
type VendorSpec []VendorSpecItem

func (v VendorSpec) Value() (driver.Value, error) {
	if v == nil {
		return nil, nil
	}
	return json.Marshal(v)
}

func (v *VendorSpec) Scan(value interface{}) error {
	if value == nil {
		*v = nil
		return nil
	}
	var bytes []byte
	switch val := value.(type) {
	case []byte:
		bytes = val
	case string:
		bytes = []byte(val)
	default:
		return errors.New("type assertion failed for VendorSpec")
	}
	return json.Unmarshal(bytes, v)
}

// LocalQtSetting caches server-pushed settings from qt_settings (MariaDB) into local SQLite.
// Synced during component sync. Each row is a JSON-valued setting identified by name.
type LocalQtSetting struct {
	Name  string `gorm:"primaryKey;size:100"`
	Value string `gorm:"type:text"`
}

func (LocalQtSetting) TableName() string { return "local_qt_settings" }
