package schema

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"yh_web/server/config"

	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

// 应用需要的集合及索引（与 MongoDB/create_collections.js、sql/init.sql 一致）
var requiredCollections = map[string][]indexSpec{
	"sites":         {{Keys: bson.D{{Key: "created_at", Value: -1}}, Name: "idx_created_at"}},
	"pages":         {{Keys: bson.D{{Key: "site_id", Value: 1}, {Key: "slug", Value: 1}}, Name: "idx_site_slug", Unique: true}},
	"site_assets":   {{Keys: bson.D{{Key: "site_id", Value: 1}}, Name: "idx_site_id"}},
	"users":         {{Keys: bson.D{{Key: "username", Value: 1}}, Name: "idx_username", Unique: true}, {Keys: bson.D{{Key: "mobile", Value: 1}}, Name: "idx_mobile", Sparse: true}},
	"workspaces":    {{Keys: bson.D{{Key: "user_id", Value: 1}}, Name: "idx_user_id"}},
	"conversations": {{Keys: bson.D{{Key: "user_id", Value: 1}}, Name: "idx_user_id"}, {Keys: bson.D{{Key: "workspace_id", Value: 1}}, Name: "idx_workspace_id"}},
	"messages":      {{Keys: bson.D{{Key: "conversation_id", Value: 1}}, Name: "idx_conversation_id"}},
	"files":             {{Keys: bson.D{{Key: "user_id", Value: 1}}, Name: "idx_user_id"}},
	"system_config":    {},
	"role_permissions": {{Keys: bson.D{{Key: "role_id", Value: 1}}, Name: "idx_role_id", Unique: true}},
}

type indexSpec struct {
	Keys   bson.D
	Name   string
	Unique bool
	Sparse bool
}

// 每个集合对应的单表 SQL DDL（仅 CREATE TABLE 段落，用于 created_*.sql）；反引号用 \x60 表示
var tableDDL = map[string]string{
	"sites": "CREATE TABLE IF NOT EXISTS \x60sites\x60 (\n  \x60id\x60          VARCHAR(24)  NOT NULL COMMENT '主键，与 MongoDB ObjectID 字符串一致',\n  \x60name\x60        VARCHAR(255) NOT NULL DEFAULT '' COMMENT '站点名称',\n  \x60domain\x60      VARCHAR(255) NOT NULL DEFAULT '' COMMENT '域名',\n  \x60description\x60 TEXT                 COMMENT '描述',\n  \x60created_at\x60  VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_created_at\x60 (\x60created_at\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='站点表';",
	"pages": "CREATE TABLE IF NOT EXISTS \x60pages\x60 (\n  \x60id\x60         VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60site_id\x60    VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '站点ID',\n  \x60slug\x60       VARCHAR(128) NOT NULL DEFAULT '' COMMENT '路径标识 index, about, ...',\n  \x60title\x60      VARCHAR(255) NOT NULL DEFAULT '' COMMENT '标题',\n  \x60type\x60       VARCHAR(32)  NOT NULL DEFAULT 'page' COMMENT '类型 homepage, page',\n  \x60content\x60    LONGTEXT              COMMENT 'HTML 或 JSON 字符串',\n  \x60updated_at\x60 VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '更新时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_site_slug\x60 (\x60site_id\x60, \x60slug\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='网页表';",
	"site_assets": "CREATE TABLE IF NOT EXISTS \x60site_assets\x60 (\n  \x60id\x60           VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60site_id\x60      VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '站点ID',\n  \x60name\x60         VARCHAR(255) NOT NULL DEFAULT '' COMMENT '文件名/显示名',\n  \x60file_path\x60    VARCHAR(512) NOT NULL DEFAULT '' COMMENT '相对路径',\n  \x60size\x60         BIGINT       NOT NULL DEFAULT 0 COMMENT '字节数',\n  \x60content_type\x60 VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'MIME 类型',\n  \x60created_at\x60   VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_site_id\x60 (\x60site_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='站点资源表';",
	"users": "CREATE TABLE IF NOT EXISTS \x60users\x60 (\n  \x60id\x60               VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60username\x60         VARCHAR(64)  NOT NULL DEFAULT '' COMMENT '用户名',\n  \x60mobile\x60           VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '手机号',\n  \x60email\x60            VARCHAR(128) NOT NULL DEFAULT '' COMMENT '邮箱',\n  \x60password\x60         VARCHAR(255) NOT NULL DEFAULT '' COMMENT '密码哈希',\n  \x60role\x60             VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '角色名',\n  \x60role_id\x60          INT          NOT NULL DEFAULT 1 COMMENT '9527=超级管理员 1=普通用户',\n  \x60is_beta\x60          TINYINT(1)   NOT NULL DEFAULT 0 COMMENT '是否体验用户',\n  \x60trial_start_date\x60 VARCHAR(16)  NOT NULL DEFAULT '' COMMENT '试用开始日期',\n  \x60trial_end_date\x60   VARCHAR(16)  NOT NULL DEFAULT '' COMMENT '试用结束日期',\n  \x60last_login\x60       VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '最后登录时间',\n  \x60llm\x60              VARCHAR(64)  NOT NULL DEFAULT '' COMMENT 'LLM 配置',\n  PRIMARY KEY (\x60id\x60),\n  UNIQUE KEY \x60uk_username\x60 (\x60username\x60),\n  KEY \x60idx_mobile\x60 (\x60mobile\x60),\n  KEY \x60idx_email\x60 (\x60email\x60),\n  KEY \x60idx_role_id\x60 (\x60role_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';",
	"workspaces": "CREATE TABLE IF NOT EXISTS \x60workspaces\x60 (\n  \x60id\x60         VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60name\x60       VARCHAR(255) NOT NULL DEFAULT '' COMMENT '名称',\n  \x60user_id\x60    VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '用户ID',\n  \x60created_at\x60 VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_user_id\x60 (\x60user_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作空间表';",
	"conversations": "CREATE TABLE IF NOT EXISTS \x60conversations\x60 (\n  \x60id\x60           VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60title\x60        VARCHAR(255) NOT NULL DEFAULT '' COMMENT '标题',\n  \x60user_id\x60      VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '用户ID',\n  \x60workspace_id\x60 VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '工作空间ID',\n  \x60created_at\x60   VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  \x60updated_at\x60   VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '更新时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_user_id\x60 (\x60user_id\x60),\n  KEY \x60idx_workspace_id\x60 (\x60workspace_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='对话表';",
	"messages": "CREATE TABLE IF NOT EXISTS \x60messages\x60 (\n  \x60id\x60              VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60conversation_id\x60 VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '对话ID',\n  \x60role\x60            VARCHAR(16)  NOT NULL DEFAULT '' COMMENT 'role: user/assistant/system',\n  \x60content\x60         LONGTEXT              COMMENT '内容',\n  \x60created_at\x60      VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_conversation_id\x60 (\x60conversation_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消息表';",
	"files": "CREATE TABLE IF NOT EXISTS \x60files\x60 (\n  \x60id\x60         VARCHAR(24)  NOT NULL COMMENT '主键',\n  \x60user_id\x60    VARCHAR(24)  NOT NULL DEFAULT '' COMMENT '用户ID',\n  \x60name\x60       VARCHAR(255) NOT NULL DEFAULT '' COMMENT '文件名',\n  \x60file_path\x60  VARCHAR(512) NOT NULL DEFAULT '' COMMENT '存储路径',\n  \x60size\x60       BIGINT       NOT NULL DEFAULT 0 COMMENT '字节数',\n  \x60created_at\x60 VARCHAR(32)  NOT NULL DEFAULT '' COMMENT '创建时间',\n  PRIMARY KEY (\x60id\x60),\n  KEY \x60idx_user_id\x60 (\x60user_id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表';",
	"system_config": "CREATE TABLE IF NOT EXISTS \x60system_config\x60 (\n  \x60id\x60        VARCHAR(64)  NOT NULL COMMENT '配置键 payment, sms_platform 等',\n  \x60payload\x60   JSON                  COMMENT '配置内容（支付/短信等）',\n  \x60updated_at\x60 VARCHAR(32) NOT NULL DEFAULT '' COMMENT '更新时间',\n  PRIMARY KEY (\x60id\x60)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统配置表';",
}

// CollectionInfo 线上集合信息（名称、文档数、索引）
type CollectionInfo struct {
	Name    string   `json:"name"`
	Count   int64    `json:"count"`
	Indexes []string `json:"indexes"` // 索引名或 key 描述
}

// Sync 连接 MongoDB 后调用：获取线上表结构并生成 sql；缺失的集合在线上创建，并生成本次创建的 sql
// projectRoot 为项目根目录（sql/ 所在目录）
func Sync(ctx context.Context, projectRoot string) error {
	db := config.GetDB(config.DBName)
	if db == nil {
		return nil
	}

	sqlDir := filepath.Join(projectRoot, "sql")
	_ = os.MkdirAll(sqlDir, 0755)

	// 1. 获取当前已有集合
	existing, err := db.ListCollectionNames(ctx, bson.M{})
	if err != nil {
		return fmt.Errorf("列出集合失败: %w", err)
	}
	existingSet := make(map[string]bool)
	for _, n := range existing {
		existingSet[n] = true
	}

	// 2. 缺失的集合在线上创建（集合 + 索引）
	var created []string
	for collName, idxSpecs := range requiredCollections {
		if !existingSet[collName] {
			if err := db.CreateCollection(ctx, collName); err != nil {
				return fmt.Errorf("创建集合 %s 失败: %w", collName, err)
			}
			created = append(created, collName)
			coll := db.Collection(collName)
			for _, spec := range idxSpecs {
				opts := options.Index().SetName(spec.Name)
				if spec.Unique {
					opts.SetUnique(true)
				}
				if spec.Sparse {
					opts.SetSparse(true)
				}
				_, _ = coll.Indexes().CreateOne(ctx, mongo.IndexModel{Keys: spec.Keys, Options: opts})
			}
			existingSet[collName] = true
		}
	}
	sort.Strings(created)

	// 3. 拉取线上结构：每个集合的名称、文档数、索引列表
	allNames := make([]string, 0, len(existingSet))
	for n := range existingSet {
		allNames = append(allNames, n)
	}
	sort.Strings(allNames)

	infos := make([]CollectionInfo, 0, len(allNames))
	for _, name := range allNames {
		coll := db.Collection(name)
		count, _ := coll.CountDocuments(ctx, bson.M{})
		indexLines := listIndexes(ctx, coll)
		infos = append(infos, CollectionInfo{Name: name, Count: count, Indexes: indexLines})
	}

	ts := time.Now().Format("20060102_150405")

	// 4. 生成「线上表结构」的 sql 文件
	if err := writeOnlineSchemaSQL(sqlDir, config.DBName, ts, infos); err != nil {
		return err
	}

	// 5. 若有本次创建的集合，生成「需要创建的 sql」并在本地写入（线上已通过上面 CreateCollection 创建）
	if len(created) > 0 {
		if err := writeCreatedSQL(sqlDir, ts, created); err != nil {
			return err
		}
	}

	return nil
}

func listIndexes(ctx context.Context, coll *mongo.Collection) []string {
	cursor, err := coll.Indexes().List(ctx)
	if err != nil {
		return nil
	}
	defer cursor.Close(ctx)

	var lines []string
	for cursor.Next(ctx) {
		var spec bson.M
		if err := cursor.Decode(&spec); err != nil {
			continue
		}
		name, _ := spec["name"].(string)
		key, _ := spec["key"].(bson.M)
		if name != "" {
			lines = append(lines, fmt.Sprintf("%s: %v", name, key))
		}
	}
	return lines
}

func writeOnlineSchemaSQL(sqlDir, dbName, ts string, infos []CollectionInfo) error {
	var b strings.Builder
	b.WriteString("-- 线上 MongoDB 表结构快照（对应数据库: " + dbName + "）\n")
	b.WriteString("-- 生成时间: " + time.Now().Format("2006-01-02 15:04:05") + "\n")
	b.WriteString("-- 以下为集合与索引说明；等效 MySQL 建表见 sql/init.sql\n\n")
	b.WriteString("SET NAMES utf8mb4;\n\n")

	for _, info := range infos {
		b.WriteString("-- -------------------------------\n")
		b.WriteString("-- 集合: " + info.Name + " (文档数: " + fmt.Sprintf("%d", info.Count) + ")\n")
		b.WriteString("-- -------------------------------\n")
		for _, idx := range info.Indexes {
			b.WriteString("-- 索引: " + idx + "\n")
		}
		if len(info.Indexes) == 0 {
			b.WriteString("-- 索引: _id\n")
		}
		b.WriteString("\n")
		// 附上等效 CREATE TABLE（便于对照）
		if ddl, ok := tableDDL[info.Name]; ok {
			b.WriteString(ddl + "\n\n")
		}
	}

	fpath := filepath.Join(sqlDir, "online_schema_"+ts+".sql")
	return os.WriteFile(fpath, []byte(b.String()), 0644)
}

func writeCreatedSQL(sqlDir, ts string, created []string) error {
	var b strings.Builder
	b.WriteString("-- 本次启动时在线上缺失并已创建的集合，对应 SQL 建表（MySQL 等效）\n")
	b.WriteString("-- 生成时间: " + time.Now().Format("2006-01-02 15:04:05") + "\n")
	b.WriteString("-- 线上 MongoDB 已通过 CreateCollection 创建；本文件供留档与 SQL 环境对照。\n\n")
	b.WriteString("SET NAMES utf8mb4;\n\n")

	for _, name := range created {
		if ddl, ok := tableDDL[name]; ok {
			b.WriteString("-- " + name + "\n")
			b.WriteString(ddl + "\n\n")
		}
	}

	fpath := filepath.Join(sqlDir, "created_"+ts+".sql")
	return os.WriteFile(fpath, []byte(b.String()), 0644)
}
