package applogic

import (
	"context"
	"fmt"
	"log"
	"strings"
	"sync"

	"aijianzhan/platform/internal/agentstore"
	"aijianzhan/platform/internal/dbsync"
	"aijianzhan/platform/internal/schema"
	"aijianzhan/platform/internal/svc"
)

// HealResult Z12h 自愈结果。
type HealResult struct {
	ChannelID      string `json:"channel_id"`
	BindingsFixed  int    `json:"bindings_fixed"`
	AgentsFixed    int    `json:"agents_fixed"`
	ChannelCreated bool   `json:"channel_created,omitempty"`
}

var healTenantMu sync.Map // tenantID -> *sync.Mutex；避免 SyncPage 并行列表双建默认通道

func lockTenantHeal(tenantID int64) func() {
	v, _ := healTenantMu.LoadOrStore(tenantID, &sync.Mutex{})
	mu := v.(*sync.Mutex)
	mu.Lock()
	return mu.Unlock
}

// channelAlive 通道是否仍属于该租户。
func channelAlive(store *dbsync.FileStore, tenantID int64, channelID string) bool {
	id := strings.TrimSpace(channelID)
	if id == "" || store == nil || tenantID <= 0 {
		return false
	}
	_, err := store.GetChannelForTenant(id, tenantID)
	return err == nil
}

func agentNeedsSyncHeal(store *dbsync.FileStore, a agentstore.Account) bool {
	// Z35：非法物理库名（如中文昵称）也要纠正，否则发布 EnsureDatabase 400
	if dn := strings.TrimSpace(a.DatabaseName); dn != "" && !schema.ValidDBName(dn) {
		return true
	}
	cid := strings.TrimSpace(a.ChannelID)
	alive := channelAlive(store, a.TenantID, cid)
	if cid != "" {
		if !alive {
			return true // 挂死通道
		}
		return strings.TrimSpace(a.OnlineDBID) == "" // 通道在但缺 online
	}
	// 无通道：仅 active（与 Z12c 启用即绑一致），勿动 pending
	return a.Status == agentstore.StatusActive || a.Status == ""
}

// HealTenantSyncBind Z12h：确保公司默认同步通道存在并运行；重挂挂死通道的 Agent/Binding。
// online_db_id 有值则保留（同一 remote 上的逻辑库名不变）；仅改 channel_id。
func HealTenantSyncBind(ctx context.Context, svcCtx *svc.ServiceContext, tenantID int64) (*HealResult, error) {
	if svcCtx == nil || svcCtx.DBSync == nil || tenantID <= 0 {
		return nil, nil
	}
	unlock := lockTenantHeal(tenantID)
	defer unlock()

	cfg := svcCtx.Config.DBSync
	driver := dbsync.Driver(strings.TrimSpace(cfg.DefaultRemoteDriver))
	if driver == "" {
		driver = dbsync.DriverPostgres
	}
	before, _ := svcCtx.DBSync.Store().FindSystemDefaultChannel(tenantID)
	ch, err := svcCtx.DBSync.EnsureAndStartSystemDefaultChannel(dbsync.DefaultChannelOpts{
		TenantID:     tenantID,
		RemoteDriver: driver,
		RemoteDSN:    strings.TrimSpace(cfg.DefaultRemoteDSN),
		Name:         fmt.Sprintf("公司默认同步 #%d", tenantID),
	})
	if err != nil {
		return nil, err
	}
	out := &HealResult{
		ChannelID:      ch.ID,
		ChannelCreated: before == nil || before.ID != ch.ID,
	}
	// Binding：通道空或已删除 → 改挂默认通道（保留 online_db_id）
	list, err := svcCtx.DBSync.Store().ListBindings(tenantID, "")
	if err == nil {
		for _, b := range list {
			if channelAlive(svcCtx.DBSync.Store(), tenantID, b.ChannelID) {
				continue
			}
			b.ChannelID = ch.ID
			if strings.TrimSpace(b.OnlineDBID) == "" {
				b.OnlineDBID = dbsync.ResolveOnlineDBID("", ch.ID)
			}
			if _, e := svcCtx.DBSync.Store().EnsureBinding(b); e != nil {
				log.Printf("dbsync heal binding tenant=%d local=%s: %v", tenantID, b.LocalDatabaseID, e)
				continue
			}
			out.BindingsFixed++
		}
	}
	// Agent：挂死通道 / 缺 online / active 无通道 → AttachSyncBind（保留 online_db_id）
	if svcCtx.Agents != nil {
		agents, err := svcCtx.Agents.List(ctx, tenantID)
		if err == nil {
			for i := range agents {
				a := agents[i]
				if !agentNeedsSyncHeal(svcCtx.DBSync.Store(), a) {
					continue
				}
				online := strings.TrimSpace(a.OnlineDBID)
				if online == "" {
					online = dbsync.ResolveOnlineDBID("", ch.ID)
				}
				cid := strings.TrimSpace(a.ChannelID)
				if !channelAlive(svcCtx.DBSync.Store(), tenantID, cid) {
					cid = ch.ID
				}
				dbName := schema.SafeSyncDatabaseName(a.DatabaseName, a.AgentID, 0)
				if _, e := svcCtx.Agents.AttachSyncBind(ctx, a.AgentID, tenantID, cid, online, dbName, a.Status == agentstore.StatusActive || a.Status == ""); e != nil {
					log.Printf("dbsync heal agent tenant=%d agent=%d: %v", tenantID, a.AgentID, e)
					continue
				}
				out.AgentsFixed++
			}
		}
	}
	if out.BindingsFixed > 0 || out.AgentsFixed > 0 || out.ChannelCreated {
		log.Printf("dbsync heal tenant=%d channel=%s bindings=%d agents=%d created=%v",
			tenantID, ch.ID, out.BindingsFixed, out.AgentsFixed, out.ChannelCreated)
	}
	return out, nil
}
