package services

import (
	"log"
	"sync"
	"time"

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

// MonitorService 监控服务
type MonitorService struct {
	httpClient   *utils.HTTPClient
	storage      *storage.Storage
	stopCh       chan struct{}
	rescheduleCh chan struct{}
	running      bool
	mu           sync.Mutex
}

var (
	monitorService *MonitorService
	monitorOnce    sync.Once
)

// GetMonitorService 获取监控服务单例
func GetMonitorService() *MonitorService {
	monitorOnce.Do(func() {
		st := storage.GetStorage()
		cfg := config.GetConfig()
		monitorService = &MonitorService{
			httpClient:   utils.NewHTTPClient(cfg.Monitor.Timeout),
			storage:      st,
			stopCh:       make(chan struct{}),
			rescheduleCh: make(chan struct{}, 1),
		}
	})
	return monitorService
}

// Start 启动监控服务
func (s *MonitorService) Start() {
	s.mu.Lock()
	if s.running {
		s.mu.Unlock()
		return
	}
	s.running = true
	s.mu.Unlock()

	log.Println("监控服务已启动")
	go s.runLoop()
}

// Reschedule 在检测间隔配置变更后唤醒主循环以使用新周期（非阻塞）
func (s *MonitorService) Reschedule() {
	select {
	case s.rescheduleCh <- struct{}{}:
	default:
	}
}

func (s *MonitorService) runLoop() {
	go s.checkAll()

	d := config.GetConfig().Monitor.Interval
	if d <= 0 {
		d = 30 * time.Minute
	}
	timer := time.NewTimer(d)
	defer timer.Stop()

	for {
		select {
		case <-s.stopCh:
			if !timer.Stop() {
				select {
				case <-timer.C:
				default:
				}
			}
			log.Println("监控服务已停止")
			return
		case <-s.rescheduleCh:
			if !timer.Stop() {
				select {
				case <-timer.C:
				default:
				}
			}
			d = config.GetConfig().Monitor.Interval
			if d <= 0 {
				d = 30 * time.Minute
			}
			timer.Reset(d)
		case <-timer.C:
			go s.checkAll()
			d = config.GetConfig().Monitor.Interval
			if d <= 0 {
				d = 30 * time.Minute
			}
			timer.Reset(d)
		}
	}
}

// Stop 停止监控服务
func (s *MonitorService) Stop() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.running {
		close(s.stopCh)
		s.running = false
	}
}

// checkAll 检查所有网站（错峰执行，避免并发暴涨）
func (s *MonitorService) checkAll() {
	websites := s.storage.GetWebsites()
	semaphore := make(chan struct{}, 3) // 最多 3 个并发检测

	for i, website := range websites {
		// 每个网站之间间隔 1 秒，把检测分散开
		if i > 0 {
			time.Sleep(1 * time.Second)
		}

		var wg sync.WaitGroup
		for _, urlInfo := range website.URLs {
			wg.Add(1)
			go func(w models.Website, u models.URLInfo) {
				defer wg.Done()
				semaphore <- struct{}{}
				defer func() { <-semaphore }()
				s.checkURL(w, u)
			}(website, urlInfo)
		}
		wg.Wait()
	}

	// 检测完毕后，逐个解析 DNS
	s.resolveAllWebsiteIPs(websites)

	// 保存记录
	s.storage.SaveAll()
	log.Printf("本轮检测完成，共 %d 个网站", len(websites))
}

// resolveAllWebsiteIPs 逐个解析所有网站域名 IP（每次都刷新）
func (s *MonitorService) resolveAllWebsiteIPs(websites []models.Website) {
	for i, website := range websites {
		if len(website.URLs) == 0 {
			continue
		}
		if i > 0 {
			time.Sleep(500 * time.Millisecond)
		}
		s.resolveWebsiteIP(website)
	}
}

// resolveWebsiteIP 解析单个网站的域名 IP
func (s *MonitorService) resolveWebsiteIP(website models.Website) {
	if len(website.URLs) == 0 {
		return
	}

	if utils.IsIntranetURL(website.URLs[0].URL) {
		w := s.storage.GetWebsite(website.ID)
		if w != nil && len(w.IPAddresses) > 0 {
			w.IPAddresses = nil
			w.UpdatedAt = time.Now()
			s.storage.UpdateWebsite(*w)
		}
		return
	}

	ips, err := utils.ResolveDomainIPs(website.URLs[0].URL)
	if err != nil {
		log.Printf("DNS解析失败 [%s]: %v", website.Name, err)
		return
	}

	if len(ips) == 0 {
		return
	}

	w := s.storage.GetWebsite(website.ID)
	if w == nil {
		return
	}

	w.IPAddresses = ips
	w.UpdatedAt = time.Now()
	s.storage.UpdateWebsite(*w)
	log.Printf("DNS解析 [%s] → %v", website.Name, ips)
}

// checkURL 检查单个URL（带重试）
func (s *MonitorService) checkURL(website models.Website, urlInfo models.URLInfo) {
	cfg := config.GetConfig()
	maxRetries := cfg.Monitor.RetryCount

	var result utils.CheckResult

	for attempt := 0; attempt <= maxRetries; attempt++ {
		if attempt > 0 {
			time.Sleep(time.Duration(attempt) * 2 * time.Second)
			log.Printf("重试 [%s] %s - 第 %d 次重试", website.Name, urlInfo.URL, attempt)
		}

		result = s.httpClient.CheckWebsiteStatus(urlInfo.URL)
		if result.Error == nil && utils.IsSuccessStatus(result.StatusCode) {
			break
		}
	}

	record := models.MonitorRecord{
		WebsiteID:  website.ID,
		URLID:      urlInfo.ID,
		URL:        urlInfo.URL,
		StatusCode: result.StatusCode,
		Latency:    result.Latency.Milliseconds(),
		IsUp:       result.Error == nil && utils.IsSuccessStatus(result.StatusCode),
		CheckedAt:  time.Now(),
	}

	if result.Error != nil {
		record.Error = result.Error.Error()
	}

	s.storage.AddRecord(record)

	// 仅当网站无标题时才做完整检测来获取元数据
	if record.IsUp && website.Title == "" {
		fullResult := s.httpClient.CheckWebsite(urlInfo.URL)
		if fullResult.Title != "" {
			w := s.storage.GetWebsite(website.ID)
			if w != nil {
				w.Title = fullResult.Title
				w.UpdatedAt = time.Now()
				s.storage.UpdateWebsite(*w)
			}
		}
	}

	log.Printf("检测 [%s] %s - 状态码: %d, 延迟: %dms, 可用: %v",
		website.Name, urlInfo.URL, result.StatusCode, result.Latency.Milliseconds(), record.IsUp)
}

// CheckWebsiteNow 立即检查指定网站（状态 + DNS，等待完成后保存）
func (s *MonitorService) CheckWebsiteNow(websiteID string) {
	website := s.storage.GetWebsite(websiteID)
	if website == nil {
		return
	}

	// 逐个检测该网站的所有 URL
	for _, urlInfo := range website.URLs {
		s.checkURL(*website, urlInfo)
	}

	// 刷新 DNS
	s.resolveWebsiteIP(*website)

	s.storage.SaveAll()
}

// GetWebsiteStatus 获取网站状态
func (s *MonitorService) GetWebsiteStatus(websiteID string) *models.WebsiteStatus {
	website := s.storage.GetWebsite(websiteID)
	if website == nil {
		return nil
	}

	isIntranet := len(website.URLs) > 0 && utils.IsIntranetURL(website.URLs[0].URL)
	status := &models.WebsiteStatus{
		Website:     *website,
		IsIntranet:  isIntranet,
		URLStatuses: []models.URLStatus{},
	}

	now := time.Now()
	since24h := now.Add(-24 * time.Hour)
	since7d := now.Add(-7 * 24 * time.Hour)
	since90d := now.Add(-90 * 24 * time.Hour)

	var totalUptime24h, totalUptime7d float64
	var urlCount int

	for _, urlInfo := range website.URLs {
		urlStatus := s.getURLStatus(website.ID, urlInfo, since24h, since7d)
		status.URLStatuses = append(status.URLStatuses, urlStatus)

		totalUptime24h += urlStatus.Uptime24h
		totalUptime7d += urlStatus.Uptime7d
		urlCount++
	}

	if urlCount > 0 {
		status.Uptime24h = totalUptime24h / float64(urlCount)
		status.Uptime7d = totalUptime7d / float64(urlCount)
	}

	status.DailyHistory = s.storage.GetWebsiteDailyAggregates(website.ID, since90d)
	if pt, ut := s.storage.GetWebsiteDayProbeTotals(website.ID, since90d); pt > 0 {
		status.Uptime90d = float64(ut) / float64(pt) * 100
	}

	// 获取最后检测时间
	for _, urlStatus := range status.URLStatuses {
		if urlStatus.CurrentState.CheckedAt.After(status.LastChecked) {
			status.LastChecked = urlStatus.CurrentState.CheckedAt
		}
	}

	return status
}

// getURLStatus 获取URL状态：每个 URL 仅 1 次读小时表 + 1 次读 latest（在内存拆 24h/7d）
func (s *MonitorService) getURLStatus(websiteID string, urlInfo models.URLInfo, since24h, since7d time.Time) models.URLStatus {
	var latest *models.MonitorRecord
	if rec := s.storage.GetLatestRecord(websiteID, urlInfo.ID); rec != nil {
		latest = rec
	}
	hRows := s.storage.GetProbeHourRowsForURL(websiteID, urlInfo.ID, since7d)
	return s.buildURLStatusFromHours(websiteID, urlInfo, since24h, since7d, latest, hRows)
}

func hourlyStatFromProbeRow(h storage.MonitorProbeHour) models.HourlyStats {
	st := models.HourlyStats{
		Hour:       h.HourAt,
		TotalCount: h.ProbeCount,
		UpCount:    h.UpCount,
	}
	if h.ProbeCount > 0 {
		st.AvgLatency = h.LatencySum / int64(h.ProbeCount)
		st.Uptime = float64(h.UpCount) / float64(h.ProbeCount) * 100
	}
	return st
}

func hourWindowsFromRows(rows []storage.MonitorProbeHour, since24h, since7d time.Time) (
	stats24h []models.HourlyStats,
	stats7d []models.HourlyStats,
	t24, u24, l24, t7, u7 int64,
) {
	for _, h := range rows {
		if !h.HourAt.Before(since7d) {
			t7 += int64(h.ProbeCount)
			u7 += int64(h.UpCount)
			stats7d = append(stats7d, hourlyStatFromProbeRow(h))
		}
		if !h.HourAt.Before(since24h) {
			t24 += int64(h.ProbeCount)
			u24 += int64(h.UpCount)
			l24 += h.LatencySum
			stats24h = append(stats24h, hourlyStatFromProbeRow(h))
		}
	}
	return stats24h, stats7d, t24, u24, l24, t7, u7
}

func (s *MonitorService) buildURLStatusFromHours(
	websiteID string,
	urlInfo models.URLInfo,
	since24h, since7d time.Time,
	latest *models.MonitorRecord,
	hRows []storage.MonitorProbeHour,
) models.URLStatus {
	urlStatus := models.URLStatus{URLInfo: urlInfo}
	if latest != nil {
		urlStatus.CurrentState = *latest
	}
	s24, s7, t24, u24, l24, t7, u7 := hourWindowsFromRows(hRows, since24h, since7d)
	urlStatus.History24h = hourlyStatsToMonitorRecords(websiteID, urlInfo.ID, s24)
	if t24 > 0 {
		urlStatus.Uptime24h = float64(u24) / float64(t24) * 100
		urlStatus.AvgLatency = l24 / t24
	}
	urlStatus.History7d = s7
	if t7 > 0 {
		urlStatus.Uptime7d = float64(u7) / float64(t7) * 100
	}
	return urlStatus
}

func hourlyStatsToMonitorRecords(websiteID, urlID string, stats []models.HourlyStats) []models.MonitorRecord {
	out := make([]models.MonitorRecord, 0, len(stats))
	for _, h := range stats {
		isUp := h.TotalCount > 0 && h.UpCount*2 >= h.TotalCount
		out = append(out, models.MonitorRecord{
			WebsiteID: websiteID,
			URLID:     urlID,
			Latency:   h.AvgLatency,
			IsUp:      isUp,
			CheckedAt: h.Hour,
		})
	}
	return out
}

// GetAllWebsiteStatuses 获取所有网站状态（批量查 probe 表，避免每个站点/URL 多次 SQL）
func (s *MonitorService) GetAllWebsiteStatuses() []models.WebsiteStatus {
	websites := s.storage.GetWebsites()
	if len(websites) == 0 {
		return nil
	}

	now := time.Now()
	since24h := now.Add(-24 * time.Hour)
	since7d := now.Add(-7 * 24 * time.Hour)
	since90d := now.Add(-90 * 24 * time.Hour)
	since90dDay := time.Date(since90d.Year(), since90d.Month(), since90d.Day(), 0, 0, 0, 0, since90d.Location())

	latestMap := s.storage.GetProbeLatestKeyMap()
	hourMap := s.storage.GroupProbeHoursSince(since7d)
	dailyBySite, uptime90dBySite := s.storage.LoadAllWebsiteDayRollups(since90dDay)

	statuses := make([]models.WebsiteStatus, 0, len(websites))
	for _, w := range websites {
		daily := dailyBySite[w.ID]
		u90 := uptime90dBySite[w.ID]
		st := s.buildWebsiteStatusBulk(w, since24h, since7d, latestMap, hourMap, daily, u90)
		if st != nil {
			statuses = append(statuses, *st)
		}
	}
	return statuses
}

func (s *MonitorService) buildWebsiteStatusBulk(
	w models.Website,
	since24h, since7d time.Time,
	latestMap map[string]models.MonitorRecord,
	hourMap map[string]map[string][]storage.MonitorProbeHour,
	daily []models.DailyStats,
	uptime90d float64,
) *models.WebsiteStatus {
	isIntranet := len(w.URLs) > 0 && utils.IsIntranetURL(w.URLs[0].URL)
	status := &models.WebsiteStatus{
		Website:     w,
		IsIntranet:  isIntranet,
		URLStatuses: []models.URLStatus{},
	}

	siteHours := hourMap[w.ID]
	if siteHours == nil {
		siteHours = map[string][]storage.MonitorProbeHour{}
	}

	var totalUptime24h, totalUptime7d float64
	urlCount := 0
	for _, urlInfo := range w.URLs {
		key := w.ID + "\x00" + urlInfo.ID
		var latest *models.MonitorRecord
		if rec, ok := latestMap[key]; ok {
			r := rec
			latest = &r
		}
		hRows := siteHours[urlInfo.ID]
		urlSt := s.buildURLStatusFromHours(w.ID, urlInfo, since24h, since7d, latest, hRows)
		status.URLStatuses = append(status.URLStatuses, urlSt)
		totalUptime24h += urlSt.Uptime24h
		totalUptime7d += urlSt.Uptime7d
		urlCount++
	}
	if urlCount > 0 {
		status.Uptime24h = totalUptime24h / float64(urlCount)
		status.Uptime7d = totalUptime7d / float64(urlCount)
	}
	if daily != nil {
		status.DailyHistory = daily
	} else {
		status.DailyHistory = []models.DailyStats{}
	}
	status.Uptime90d = uptime90d
	for _, urlSt := range status.URLStatuses {
		if urlSt.CurrentState.CheckedAt.After(status.LastChecked) {
			status.LastChecked = urlSt.CurrentState.CheckedAt
		}
	}
	return status
}
