package storage

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"gorm.io/datatypes"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"mengyaping-backend/config"
	"mengyaping-backend/models"
)

// Storage 基于 MySQL 的持久化（站点 / 分类 / 检测记录）
type Storage struct {
	db *gorm.DB
}

var (
	store *Storage
	once  sync.Once
)

// GetStorage 初始化数据库连接并迁移表结构
func GetStorage() *Storage {
	once.Do(func() {
		cfg := config.GetConfig()
		dsn := cfg.DatabaseDSN()

		gormLog := logger.Default.LogMode(logger.Warn)
		if os.Getenv("DB_DEBUG") == "1" {
			gormLog = logger.Default.LogMode(logger.Info)
		}

		db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
			Logger: gormLog,
		})
		if err != nil {
			log.Fatalf("MySQL 连接失败: %v（请检查 DB_* / DB_DSN 环境变量）", err)
		}
		sqlDB, err := db.DB()
		if err != nil {
			log.Fatalf("MySQL sqlDB: %v", err)
		}
		sqlDB.SetMaxIdleConns(8)
		sqlDB.SetMaxOpenConns(40)
		sqlDB.SetConnMaxLifetime(time.Hour)

		store = &Storage{db: db}
		if err := store.migrate(); err != nil {
			log.Fatalf("数据库迁移失败: %v", err)
		}
		store.seedAdminTokenKV()
		store.loadAndSyncAppConfig()
		store.seedDefaultGroups()
		log.Println("MySQL 存储已就绪: " + cfg.Database.Database + "@" + cfg.Database.Host)
	})
	return store
}

func (s *Storage) migrate() error {
	err := s.db.AutoMigrate(
		&MonitorGroup{},
		&MonitorWebsite{},
		&MonitorWebsiteURL{},
		&MonitorWebsiteGroup{},
		&MonitorProbeLatest{},
		&MonitorProbeHour{},
		&MonitorProbeDay{},
		&MonitorKV{},
	)
	if err != nil {
		return err
	}
	// 旧版逐条 monitor_checks 表体积极大，已弃用
	if s.db.Migrator().HasTable("monitor_checks") {
		if err := s.db.Migrator().DropTable("monitor_checks"); err != nil {
			log.Printf("删除旧表 monitor_checks: %v（可手工 DROP）", err)
		} else {
			log.Println("已删除旧表 monitor_checks")
		}
	}
	return nil
}

func (s *Storage) seedDefaultGroups() {
	var n int64
	s.db.Model(&MonitorGroup{}).Count(&n)
	if n > 0 {
		return
	}
	order := 0
	for _, g := range models.DefaultGroups {
		order++
		s.db.Create(&MonitorGroup{
			ID:        g.ID,
			Name:      g.Name,
			SortOrder: order,
		})
	}
	log.Println("已写入默认分类 monitor_groups")
}

func ipsToJSON(ips []string) []byte {
	if ips == nil {
		ips = []string{}
	}
	b, err := json.Marshal(ips)
	if err != nil {
		return []byte("[]")
	}
	return b
}

func jsonToIPs(raw []byte) []string {
	if len(raw) == 0 {
		return nil
	}
	var ips []string
	if err := json.Unmarshal(raw, &ips); err != nil {
		return nil
	}
	return ips
}

func rowToWebsite(w *MonitorWebsite) models.Website {
	groups := make([]string, 0, len(w.GroupLinks))
	for _, l := range w.GroupLinks {
		groups = append(groups, l.GroupID)
	}
	if len(groups) == 0 && w.LegacyGroup != "" {
		groups = []string{w.LegacyGroup}
	}
	urls := make([]models.URLInfo, 0, len(w.URLs))
	for _, u := range w.URLs {
		urls = append(urls, models.URLInfo{
			ID:     u.URLID,
			URL:    u.URL,
			Remark: u.Remark,
		})
	}
	return models.Website{
		ID:          w.ID,
		Name:        w.Name,
		Groups:      groups,
		Group:       "",
		URLs:        urls,
		IPAddresses: jsonToIPs(w.IPAddresses),
		Favicon:     w.Favicon,
		Title:       w.Title,
		CreatedAt:   w.CreatedAt,
		UpdatedAt:   w.UpdatedAt,
	}
}

func websiteToRow(w *models.Website) *MonitorWebsite {
	return &MonitorWebsite{
		ID:          w.ID,
		Name:        w.Name,
		LegacyGroup: "",
		Favicon:     w.Favicon,
		Title:       w.Title,
		IPAddresses: datatypes.JSON(ipsToJSON(w.IPAddresses)),
		CreatedAt:   w.CreatedAt,
		UpdatedAt:   w.UpdatedAt,
	}
}

func (s *Storage) listWebsiteModels() ([]models.Website, error) {
	var rows []MonitorWebsite
	if err := s.db.Preload("URLs", func(db *gorm.DB) *gorm.DB {
		return db.Order("sort_order ASC, id ASC")
	}).Preload("GroupLinks").Order("created_at ASC").Find(&rows).Error; err != nil {
		return nil, err
	}
	out := make([]models.Website, len(rows))
	for i := range rows {
		out[i] = rowToWebsite(&rows[i])
	}
	return out, nil
}

// GetWebsites 获取所有网站
func (s *Storage) GetWebsites() []models.Website {
	list, err := s.listWebsiteModels()
	if err != nil {
		log.Printf("GetWebsites: %v", err)
		return nil
	}
	return list
}

// GetWebsite 获取单个网站
func (s *Storage) GetWebsite(id string) *models.Website {
	var w MonitorWebsite
	if err := s.db.Preload("URLs", func(db *gorm.DB) *gorm.DB {
		return db.Order("sort_order ASC, id ASC")
	}).Preload("GroupLinks").First(&w, "id = ?", id).Error; err != nil {
		return nil
	}
	m := rowToWebsite(&w)
	return &m
}

func (s *Storage) persistWebsiteFull(w models.Website) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		row := websiteToRow(&w)
		if err := tx.Save(row).Error; err != nil {
			return err
		}
		if err := tx.Where("website_id = ?", w.ID).Delete(&MonitorWebsiteURL{}).Error; err != nil {
			return err
		}
		if err := tx.Where("website_id = ?", w.ID).Delete(&MonitorWebsiteGroup{}).Error; err != nil {
			return err
		}
		for i, u := range w.URLs {
			if err := tx.Create(&MonitorWebsiteURL{
				WebsiteID: w.ID,
				URLID:     u.ID,
				URL:       u.URL,
				Remark:    u.Remark,
				SortOrder: i,
			}).Error; err != nil {
				return err
			}
		}
		for _, gid := range w.Groups {
			if err := tx.Create(&MonitorWebsiteGroup{WebsiteID: w.ID, GroupID: gid}).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

// AddWebsite 添加网站
func (s *Storage) AddWebsite(website models.Website) error {
	return s.persistWebsiteFull(website)
}

// UpdateWebsite 更新网站
func (s *Storage) UpdateWebsite(website models.Website) error {
	return s.persistWebsiteFull(website)
}

// DeleteWebsite 删除网站
func (s *Storage) DeleteWebsite(id string) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		_ = tx.Where("website_id = ?", id).Delete(&MonitorProbeLatest{})
		_ = tx.Where("website_id = ?", id).Delete(&MonitorProbeHour{})
		_ = tx.Where("website_id = ?", id).Delete(&MonitorProbeDay{})
		_ = tx.Where("website_id = ?", id).Delete(&MonitorWebsiteURL{})
		_ = tx.Where("website_id = ?", id).Delete(&MonitorWebsiteGroup{})
		return tx.Where("id = ?", id).Delete(&MonitorWebsite{}).Error
	})
}

// GetGroups 获取所有分组
func (s *Storage) GetGroups() []models.Group {
	var rows []MonitorGroup
	s.db.Order("sort_order ASC, id ASC").Find(&rows)
	out := make([]models.Group, len(rows))
	for i, r := range rows {
		out[i] = models.Group{ID: r.ID, Name: r.Name}
	}
	return out
}

// AddGroup 添加分组
func (s *Storage) AddGroup(group models.Group) error {
	var n int64
	s.db.Model(&MonitorGroup{}).Count(&n)
	return s.db.Create(&MonitorGroup{
		ID:        group.ID,
		Name:      group.Name,
		SortOrder: int(n) + 1,
	}).Error
}

// GetGroupByID 按 ID 获取分组（无则 nil）
func (s *Storage) GetGroupByID(id string) *models.Group {
	var r MonitorGroup
	if err := s.db.First(&r, "id = ?", id).Error; err != nil {
		return nil
	}
	g := models.Group{ID: r.ID, Name: r.Name}
	return &g
}

// UpdateGroup 更新分组显示名称（ID 不变）
func (s *Storage) UpdateGroup(id, name string) error {
	name = strings.TrimSpace(name)
	if name == "" {
		return fmt.Errorf("分类名称不能为空")
	}
	res := s.db.Model(&MonitorGroup{}).Where("id = ?", id).Update("name", name)
	if res.Error != nil {
		return res.Error
	}
	if res.RowsAffected == 0 {
		return fmt.Errorf("分类不存在")
	}
	return nil
}

// DeleteGroup 删除分组；从所有网站中移除该分类；若网站无剩余分类则归入删除后列表中的第一个分类。至少保留一个分类。
func (s *Storage) DeleteGroup(id string) error {
	var total int64
	s.db.Model(&MonitorGroup{}).Count(&total)
	if total <= 1 {
		return fmt.Errorf("至少保留一个分类")
	}

	var remain []MonitorGroup
	s.db.Where("id != ?", id).Order("sort_order ASC, id ASC").Find(&remain)
	if len(remain) == 0 {
		return fmt.Errorf("至少保留一个分类")
	}
	fallbackID := remain[0].ID

	var sites []MonitorWebsite
	if err := s.db.Preload("GroupLinks").Find(&sites).Error; err != nil {
		return err
	}

	return s.db.Transaction(func(tx *gorm.DB) error {
		for i := range sites {
			next := make([]string, 0)
			for _, l := range sites[i].GroupLinks {
				if l.GroupID != id {
					next = append(next, l.GroupID)
				}
			}
			if len(next) == 0 {
				next = []string{fallbackID}
			}
			if err := tx.Where("website_id = ?", sites[i].ID).Delete(&MonitorWebsiteGroup{}).Error; err != nil {
				return err
			}
			for _, gid := range next {
				if err := tx.Create(&MonitorWebsiteGroup{WebsiteID: sites[i].ID, GroupID: gid}).Error; err != nil {
					return err
				}
			}
		}
		if err := tx.Delete(&MonitorGroup{}, "id = ?", id).Error; err != nil {
			return err
		}
		return nil
	})
}

// SaveAll 周期结束时清理过期汇总分区（日表按 history_days，小时表保留约 10 天）
func (s *Storage) SaveAll() error {
	s.purgeProbeRollups()
	return nil
}
