// Package dao 提供 admin 模块的数据库访问操作（会议管理）
package dao

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/echochat/backend/app/dto"
	meetingModel "github.com/echochat/backend/app/meeting/model"
	"github.com/echochat/backend/pkg/logs"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// MeetingManageDAO 管理端会议数据访问对象
//
// 设计要点：
//   - 列表查询走聚合 SQL：一次取 meeting_rooms + host 昵称 + 参与者数 + 录制数，避免 N+1
//   - 仅做"查询/统计"层操作，写操作（结束会议、删除录制）放在 service 层并复用既有路径
//   - 与 MessageManageDAO 保持一致的 ListXxx + 统计方法风格
type MeetingManageDAO struct {
	db *gorm.DB
}

// NewMeetingManageDAO 创建 MeetingManageDAO 实例
func NewMeetingManageDAO(db *gorm.DB) *MeetingManageDAO {
	return &MeetingManageDAO{db: db}
}

// MeetingListRow 列表行的聚合结果（DAO 内部结构，不外泄）
//
// 通过一次带 LEFT JOIN + GROUP BY 的查询拿齐：
//   - 主表 meeting_rooms 全部字段
//   - 主持人昵称 / 头像（auth_users）
//   - 参与者数（meeting_participants 去重 user_id）
//   - 录制数（meeting_recordings 总条目）
//
// 注意：使用 GORM Raw + Scan，绕开 model 主键自动展开，方便聚合
type MeetingListRow struct {
	ID               int64      `gorm:"column:id"`
	RoomCode         string     `gorm:"column:room_code"`
	Title            string     `gorm:"column:title"`
	Type             int        `gorm:"column:type"`
	Status           int        `gorm:"column:status"`
	HostID           int64      `gorm:"column:host_id"`
	HostNickname     string     `gorm:"column:host_nickname"`
	HostAvatar       string     `gorm:"column:host_avatar"`
	MaxMembers       int        `gorm:"column:max_members"`
	ParticipantCount int        `gorm:"column:participant_count"`
	RecordingCount   int        `gorm:"column:recording_count"`
	ScheduledAt      *time.Time `gorm:"column:scheduled_at"`
	StartedAt        *time.Time `gorm:"column:started_at"`
	EndedAt          *time.Time `gorm:"column:ended_at"`
	EndedReason      *string    `gorm:"column:ended_reason"`
	CreatedAt        time.Time  `gorm:"column:created_at"`
}

// buildListWhere 把 AdminMeetingListRequest 拼成 WHERE 条件，返回 SQL 片段与参数
// 复用给 ListMeetings + CountMeetings 两个查询，保证条件一致
func (d *MeetingManageDAO) buildListWhere(req *dto.AdminMeetingListRequest) (string, []any) {
	conditions := []string{"1=1"}
	args := []any{}

	if req.Keyword != "" {
		conditions = append(conditions, "(mr.title ILIKE ? OR mr.room_code ILIKE ?)")
		kw := fmt.Sprintf("%%%s%%", req.Keyword)
		args = append(args, kw, kw)
	}
	if req.Status != nil {
		conditions = append(conditions, "mr.status = ?")
		args = append(args, *req.Status)
	}
	if req.HostID != nil {
		conditions = append(conditions, "mr.host_id = ?")
		args = append(args, *req.HostID)
	}
	if req.StartTime != "" {
		if t, err := time.Parse("2006-01-02", req.StartTime); err == nil {
			conditions = append(conditions, "mr.created_at >= ?")
			args = append(args, t)
		}
	}
	if req.EndTime != "" {
		if t, err := time.Parse("2006-01-02", req.EndTime); err == nil {
			conditions = append(conditions, "mr.created_at < ?")
			args = append(args, t.AddDate(0, 0, 1))
		}
	}
	// HasRecording 通过 HAVING 在外层处理，这里不拼接

	where := ""
	for i, c := range conditions {
		if i == 0 {
			where = c
		} else {
			where = where + " AND " + c
		}
	}
	return where, args
}

// ListMeetings 分页查询会议列表
//
// 聚合 SQL 设计：
//   - LEFT JOIN auth_users：拿主持人昵称（host 可能已删除时为空，不影响行）
//   - LEFT JOIN meeting_participants：聚合参与者数（COUNT DISTINCT user_id）
//   - LEFT JOIN meeting_recordings：聚合录制数（COUNT recording.id）
//   - HasRecording 通过 HAVING recording_count > 0 二次过滤
//
// 排序：created_at DESC（最新会议在前）
func (d *MeetingManageDAO) ListMeetings(ctx context.Context, req *dto.AdminMeetingListRequest) ([]MeetingListRow, int64, error) {
	funcName := "dao.meeting_manage_dao.ListMeetings"
	logs.Debug(ctx, funcName, "查询会议列表")

	where, args := d.buildListWhere(req)

	page := req.Page
	if page <= 0 {
		page = 1
	}
	pageSize := req.PageSize
	if pageSize <= 0 {
		pageSize = 20
	}
	if pageSize > 100 {
		pageSize = 100
	}

	// 子查询聚合 participant_count / recording_count，避免笛卡尔积放大 COUNT
	// 这里用相关子查询而非 JOIN+GROUP BY，便于 HAVING 与 COUNT(*) 分页对齐
	baseSelect := `
		SELECT
			mr.id, mr.room_code, mr.title, mr.type, mr.status, mr.host_id,
			COALESCE(u.nickname, '') AS host_nickname,
			COALESCE(u.avatar, '') AS host_avatar,
			mr.max_members,
			COALESCE((SELECT COUNT(DISTINCT mp.user_id) FROM meeting_participants mp WHERE mp.room_id = mr.id), 0) AS participant_count,
			COALESCE((SELECT COUNT(*) FROM meeting_recordings mrec WHERE mrec.room_id = mr.id), 0) AS recording_count,
			mr.scheduled_at, mr.started_at, mr.ended_at, mr.ended_reason, mr.created_at
		FROM meeting_rooms mr
		LEFT JOIN auth_users u ON u.id = mr.host_id
		WHERE ` + where

	// HasRecording 过滤：包成外层 SELECT * FROM (...) WHERE recording_count > 0
	listSQL := baseSelect
	countSQL := `SELECT COUNT(*) FROM (` + baseSelect + `) AS sub`
	if req.HasRecording != nil && *req.HasRecording {
		listSQL = `SELECT * FROM (` + baseSelect + `) AS sub WHERE recording_count > 0`
		countSQL = `SELECT COUNT(*) FROM (` + listSQL + `) AS sub2`
	}
	listSQL += ` ORDER BY created_at DESC OFFSET ? LIMIT ?`

	var total int64
	if err := d.db.WithContext(ctx).Raw(countSQL, args...).Scan(&total).Error; err != nil {
		logs.Error(ctx, funcName, "统计会议总数失败", zap.Error(err))
		return nil, 0, err
	}

	pageArgs := append([]any{}, args...)
	pageArgs = append(pageArgs, (page-1)*pageSize, pageSize)

	var rows []MeetingListRow
	if err := d.db.WithContext(ctx).Raw(listSQL, pageArgs...).Scan(&rows).Error; err != nil {
		logs.Error(ctx, funcName, "查询会议列表失败", zap.Error(err))
		return nil, 0, err
	}

	return rows, total, nil
}

// GetMeetingByID 取单条会议聚合行（含主持人昵称 + 聚合计数）
// 供详情接口复用 MeetingListRow 投影，避免重复定义结构
func (d *MeetingManageDAO) GetMeetingByID(ctx context.Context, id int64) (*MeetingListRow, error) {
	funcName := "dao.meeting_manage_dao.GetMeetingByID"
	sql := `
		SELECT
			mr.id, mr.room_code, mr.title, mr.type, mr.status, mr.host_id,
			COALESCE(u.nickname, '') AS host_nickname,
			COALESCE(u.avatar, '') AS host_avatar,
			mr.max_members,
			COALESCE((SELECT COUNT(DISTINCT mp.user_id) FROM meeting_participants mp WHERE mp.room_id = mr.id), 0) AS participant_count,
			COALESCE((SELECT COUNT(*) FROM meeting_recordings mrec WHERE mrec.room_id = mr.id), 0) AS recording_count,
			mr.scheduled_at, mr.started_at, mr.ended_at, mr.ended_reason, mr.created_at
		FROM meeting_rooms mr
		LEFT JOIN auth_users u ON u.id = mr.host_id
		WHERE mr.id = ?
		LIMIT 1
	`
	var row MeetingListRow
	err := d.db.WithContext(ctx).Raw(sql, id).Scan(&row).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		logs.Error(ctx, funcName, "查询会议详情失败", zap.Int64("id", id), zap.Error(err))
		return nil, err
	}
	if row.ID == 0 {
		return nil, nil
	}
	return &row, nil
}

// ListParticipantsDetail 拉取会议的全部参与者（带用户昵称/头像）
//
// 按 joined_at ASC 排序，主持人先入会后退出时也按时间顺序展示，便于核对入会轨迹
func (d *MeetingManageDAO) ListParticipantsDetail(ctx context.Context, roomID int64) ([]ParticipantDetailRow, error) {
	funcName := "dao.meeting_manage_dao.ListParticipantsDetail"
	sql := `
		SELECT
			mp.user_id, mp.role, mp.joined_at, mp.left_at, mp.left_reason, mp.duration,
			COALESCE(u.nickname, '') AS nickname,
			COALESCE(u.avatar, '') AS avatar
		FROM meeting_participants mp
		LEFT JOIN auth_users u ON u.id = mp.user_id
		WHERE mp.room_id = ?
		ORDER BY mp.joined_at ASC, mp.id ASC
	`
	var rows []ParticipantDetailRow
	if err := d.db.WithContext(ctx).Raw(sql, roomID).Scan(&rows).Error; err != nil {
		logs.Error(ctx, funcName, "查询会议参与者失败", zap.Int64("room_id", roomID), zap.Error(err))
		return nil, err
	}
	return rows, nil
}

// ParticipantDetailRow 参与者明细投影
type ParticipantDetailRow struct {
	UserID     int64      `gorm:"column:user_id"`
	Role       int        `gorm:"column:role"`
	JoinedAt   time.Time  `gorm:"column:joined_at"`
	LeftAt     *time.Time `gorm:"column:left_at"`
	LeftReason *string    `gorm:"column:left_reason"`
	Duration   int        `gorm:"column:duration"`
	Nickname   string     `gorm:"column:nickname"`
	Avatar     string     `gorm:"column:avatar"`
}

// ListRecordingsByRoom 拉取会议下的全部录制（admin 视角，按 started_at DESC）
//
// 复用 meeting/model.MeetingRecording，不另起 DTO；service 层再投影成 AdminMeetingRecordingDTO
func (d *MeetingManageDAO) ListRecordingsByRoom(ctx context.Context, roomID int64) ([]meetingModel.MeetingRecording, error) {
	var list []meetingModel.MeetingRecording
	err := d.db.WithContext(ctx).
		Where("room_id = ?", roomID).
		Order("started_at DESC, id DESC").
		Find(&list).Error
	return list, err
}

// CountTotal 历史会议总数
func (d *MeetingManageDAO) CountTotal(ctx context.Context) (int64, error) {
	var count int64
	err := d.db.WithContext(ctx).Model(&meetingModel.MeetingRoom{}).Count(&count).Error
	return count, err
}

// CountActive 当前进行中（status=1）的会议数
func (d *MeetingManageDAO) CountActive(ctx context.Context) (int64, error) {
	var count int64
	err := d.db.WithContext(ctx).
		Model(&meetingModel.MeetingRoom{}).
		Where("status = ?", 1).
		Count(&count).Error
	return count, err
}

// CountToday 今日创建的会议数（按 created_at）
func (d *MeetingManageDAO) CountToday(ctx context.Context) (int64, error) {
	var count int64
	today := time.Now().Truncate(24 * time.Hour)
	err := d.db.WithContext(ctx).
		Model(&meetingModel.MeetingRoom{}).
		Where("created_at >= ?", today).
		Count(&count).Error
	return count, err
}

// CountRecordings 录制总数
func (d *MeetingManageDAO) CountRecordings(ctx context.Context) (int64, error) {
	var count int64
	err := d.db.WithContext(ctx).Model(&meetingModel.MeetingRecording{}).Count(&count).Error
	return count, err
}
