package system

import (
	"context"
	"time"

	redis "github.com/redis/go-redis/v9"
	"gorm.io/gorm"

	"security-center-backend/internal/config"
	"security-center-backend/internal/service/settings"
)

type ComponentStatus struct {
	Name          string `json:"name"`
	Status        string `json:"status"`
	LatencyMillis int64  `json:"latency_ms,omitempty"`
	Error         string `json:"error,omitempty"`
	CheckedAt     int64  `json:"checked_at"`
}

type HealthSummary struct {
	Status        string            `json:"status"`
	Components    []ComponentStatus `json:"components"`
	CheckedAt     int64             `json:"checked_at"`
	UptimeSeconds int64             `json:"uptime_seconds"`
	Version       string            `json:"version"`
}

type VersionInfo struct {
	Service   string `json:"service"`
	Version   string `json:"version"`
	Commit    string `json:"commit,omitempty"`
	BuildTime string `json:"build_time,omitempty"`
}

type Service struct {
	cfg            *config.Config
	configService  *settings.Service
	db             *gorm.DB
	redis          *redis.Client
	startedAt      time.Time
	additionalInfo VersionInfo
}

func NewService(cfg *config.Config, configService *settings.Service, db *gorm.DB, redisClient *redis.Client, startedAt time.Time) *Service {
	return &Service{
		cfg:           cfg,
		configService: configService,
		db:            db,
		redis:         redisClient,
		startedAt:     startedAt,
		additionalInfo: VersionInfo{
			Service: cfg.App.Name,
			Version: cfg.App.Version,
		},
	}
}

func (s *Service) GetConfig(ctx context.Context) (*settings.Settings, error) {
	return s.configService.GetPublic(ctx)
}

func (s *Service) GetBranding(ctx context.Context) (*settings.PlatformSettings, error) {
	snapshot, err := s.configService.GetPublic(ctx)
	if err != nil {
		return nil, err
	}
	platform := snapshot.Platform
	return &platform, nil
}

func (s *Service) UpdateConfig(ctx context.Context, input settings.UpdateInput) (*settings.Settings, error) {
	result, err := s.configService.Update(ctx, input)
	if err != nil {
		return nil, err
	}
	return settings.MaskForAPI(result), nil
}

func (s *Service) Health(ctx context.Context) *HealthSummary {
	components := []ComponentStatus{
		s.checkDatabase(ctx),
		s.checkRedis(ctx),
	}
	overall := "ok"
	for _, comp := range components {
		if comp.Status == "degraded" && overall == "ok" {
			overall = "degraded"
		}
		if comp.Status == "down" {
			overall = "down"
			break
		}
	}
	return &HealthSummary{
		Status:        overall,
		Components:    components,
		CheckedAt:     time.Now().Unix(),
		UptimeSeconds: int64(time.Since(s.startedAt).Seconds()),
		Version:       s.additionalInfo.Version,
	}
}

func (s *Service) Version() VersionInfo {
	return s.additionalInfo
}

func (s *Service) checkDatabase(ctx context.Context) ComponentStatus {
	component := ComponentStatus{
		Name:      "database",
		Status:    "skipped",
		CheckedAt: time.Now().Unix(),
	}
	if s.db == nil {
		return component
	}
	sqlDB, err := s.db.DB()
	if err != nil {
		component.Status = "down"
		component.Error = err.Error()
		return component
	}
	start := time.Now()
	if err := sqlDB.PingContext(ctx); err != nil {
		component.Status = "down"
		component.Error = err.Error()
	} else {
		component.Status = "ok"
		component.LatencyMillis = time.Since(start).Milliseconds()
	}
	return component
}

func (s *Service) checkRedis(ctx context.Context) ComponentStatus {
	component := ComponentStatus{
		Name:      "redis",
		Status:    "skipped",
		CheckedAt: time.Now().Unix(),
	}
	if s.redis == nil {
		return component
	}
	start := time.Now()
	if err := s.redis.Ping(ctx).Err(); err != nil {
		component.Status = "down"
		component.Error = err.Error()
	} else {
		component.Status = "ok"
		component.LatencyMillis = time.Since(start).Milliseconds()
	}
	return component
}
