package usecase

import (
	"backend/pkg/notification/domain/entity"
	"backend/pkg/notification/domain/notification"
	"backend/pkg/notification/domain/repository"
	"backend/pkg/notification/domain/usecase"
	"context"
	"errors"
	"fmt"
	"time"

	errs "backend/pkg/library/errors"

	"github.com/gocql/gocql"
)

// NotificationUseCaseParam 通知服務參數配置
type NotificationUseCaseParam struct {
	Repo   repository.NotificationRepository
	Logger errs.Logger
}

// NotificationUseCase 通知服務實現
type NotificationUseCase struct {
	param NotificationUseCaseParam
}

// MustNotificationUseCase 創建通知服務實例
func MustNotificationUseCase(param NotificationUseCaseParam) usecase.NotificationUseCase {
	return &NotificationUseCase{
		param: param,
	}
}

// ==================== EventUseCase 實現 ====================

// CreateEvent 創建新的通知事件
func (uc *NotificationUseCase) CreateEvent(ctx context.Context, e *usecase.NotificationEvent) error {
	// 驗證輸入
	if err := uc.validateNotificationEvent(e); err != nil {
		return err
	}

	// 轉換 priority
	priority, err := uc.parsePriority(e.Priority)
	if err != nil {
		return errs.InputInvalidRangeError(fmt.Sprintf("invalid priority: %s", e.Priority)).Wrap(err)
	}

	// 創建 entity
	event := &entity.NotificationEvent{
		EventID:    gocql.TimeUUID(),
		EventType:  e.EventType,
		ActorUID:   e.ActorUID,
		ObjectType: e.ObjectType,
		ObjectID:   e.ObjectID,
		Title:      e.Title,
		Body:       e.Body,
		Payload:    e.Payload,
		Priority:   priority,
		CreatedAt:  time.Now().UTC(),
	}

	// 保存到資料庫
	if err := uc.param.Repo.Create(ctx, event); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "event_type", Val: e.EventType},
				{Key: "actor_uid", Val: e.ActorUID},
				{Key: "func", Val: "NotificationRepository.Create"},
				{Key: "error", Val: err.Error()},
			},
			"failed to create notification event",
		).Wrap(err)
	}

	return nil
}

// GetEventByID 根據 ID 獲取事件
func (uc *NotificationUseCase) GetEventByID(ctx context.Context, id string) (*usecase.NotificationEventResp, error) {
	// 驗證 UUID 格式
	if _, err := gocql.ParseUUID(id); err != nil {
		return nil, errs.InputInvalidRangeError(fmt.Sprintf("invalid event ID format: %s", id)).Wrap(err)
	}

	// 從資料庫獲取
	event, err := uc.param.Repo.GetByID(ctx, id)
	if err != nil {
		return nil, errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "event_id", Val: id},
				{Key: "func", Val: "NotificationRepository.GetByID"},
				{Key: "error", Val: err.Error()},
			},
			"failed to get notification event by ID",
		).Wrap(err)
	}

	// 轉換為響應格式
	return uc.entityToEventResp(event), nil
}

// ListEventsByObject 根據物件查詢事件列表
func (uc *NotificationUseCase) ListEventsByObject(ctx context.Context, param usecase.QueryNotificationEventParam) ([]*usecase.NotificationEventResp, error) {
	// 驗證參數
	if param.ObjectID == nil || param.ObjectType == nil || param.Limit == nil {
		return nil, errs.InputInvalidRangeError("object_id and object_type are required")
	}

	// 構建查詢參數
	repoParam := repository.QueryNotificationEventParam{
		ObjectID:   param.ObjectID,
		ObjectType: param.ObjectType,
		Limit:      param.Limit,
	}

	// 從資料庫查詢
	events, err := uc.param.Repo.ListByObject(ctx, repoParam)
	if err != nil {
		return nil, errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "object_id", Val: *param.ObjectID},
				{Key: "object_type", Val: *param.ObjectType},
				{Key: "func", Val: "NotificationRepository.ListByObject"},
				{Key: "error", Val: err.Error()},
			},
			"failed to list notification events by object",
		).Wrap(err)
	}

	// 轉換為響應格式
	result := make([]*usecase.NotificationEventResp, 0, len(events))
	for _, event := range events {
		result = append(result, uc.entityToEvent(event))
	}

	return result, nil
}

// ==================== UserNotificationUseCase 實現 ====================

// CreateUserNotification 為單個用戶創建通知
func (uc *NotificationUseCase) CreateUserNotification(ctx context.Context, n *usecase.UserNotification) error {
	// 驗證輸入
	if err := uc.validateUserNotification(n); err != nil {
		return err
	}

	// 生成 bucket
	bucket := uc.generateBucket(time.Now().UTC())

	// 解析 EventID
	eventID, err := gocql.ParseUUID(n.EventID)
	if err != nil {
		return errs.InputInvalidRangeError(fmt.Sprintf("invalid event ID format: %s", n.EventID)).Wrap(err)
	}

	// 創建 entity
	userNotif := &entity.UserNotification{
		UserID:  n.UserID,
		Bucket:  bucket,
		TS:      gocql.TimeUUID(),
		EventID: eventID,
		Status:  notification.UNREAD,
		ReadAt:  time.Time{},
	}

	// 計算 TTL（如果未提供，使用默認值）
	ttlSeconds := n.TTL
	if ttlSeconds == 0 {
		ttlSeconds = uc.calculateDefaultTTL()
	}

	// 保存到資料庫
	if err := uc.param.Repo.CreateUserNotification(ctx, userNotif, ttlSeconds); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: n.UserID},
				{Key: "event_id", Val: n.EventID},
				{Key: "func", Val: "NotificationRepository.CreateUserNotification"},
				{Key: "error", Val: err.Error()},
			},
			"failed to create user notification",
		).Wrap(err)
	}

	return nil
}

// BulkCreateNotifications 批量創建通知
func (uc *NotificationUseCase) BulkCreateNotifications(ctx context.Context, list []*usecase.UserNotification) error {
	if len(list) == 0 {
		return errs.InputInvalidRangeError("notification list cannot be empty")
	}

	// 生成 bucket
	bucket := uc.generateBucket(time.Now().UTC())

	// 轉換為 entity 列表
	entities := make([]*entity.UserNotification, 0, len(list))
	for _, n := range list {
		// 驗證輸入
		if err := uc.validateUserNotification(n); err != nil {
			return err
		}

		// 解析 EventID
		eventID, err := gocql.ParseUUID(n.EventID)
		if err != nil {
			return errs.InputInvalidRangeError(fmt.Sprintf("invalid event ID format: %s", n.EventID)).Wrap(err)
		}

		// 計算 TTL
		ttlSeconds := n.TTL
		if ttlSeconds == 0 {
			ttlSeconds = uc.calculateDefaultTTL()
		}

		e := &entity.UserNotification{
			UserID:  n.UserID,
			Bucket:  bucket,
			TS:      gocql.TimeUUID(),
			EventID: eventID,
			Status:  notification.UNREAD,
			ReadAt:  time.Time{},
		}

		entities = append(entities, e)
	}

	// 使用第一個通知的 TTL（假設批量通知使用相同的 TTL）
	ttlSeconds := list[0].TTL
	if ttlSeconds == 0 {
		ttlSeconds = uc.calculateDefaultTTL()
	}

	// 批量保存
	if err := uc.param.Repo.BulkCreate(ctx, entities, ttlSeconds); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "count", Val: len(list)},
				{Key: "func", Val: "NotificationRepository.BulkCreate"},
				{Key: "error", Val: err.Error()},
			},
			"failed to bulk create user notifications",
		).Wrap(err)
	}

	return nil
}

// ListLatestNotifications 獲取用戶最新的通知列表
func (uc *NotificationUseCase) ListLatestNotifications(ctx context.Context, opt usecase.ListLatestOptions) ([]*usecase.UserNotificationResponse, error) {
	// 驗證參數
	if opt.UserID == "" {
		return nil, errs.InputInvalidRangeError("user_id is required")
	}

	// 限制 Limit 最大值
	if opt.Limit <= 0 {
		opt.Limit = 20 // 默認值
	}

	// 如果未提供 buckets，生成默認的 buckets（最近 3 個月）
	if len(opt.Buckets) == 0 {
		opt.Buckets = uc.generateDefaultBuckets()
	}

	// 構建查詢參數
	repoOpt := repository.ListLatestOptions{
		UserID:  opt.UserID,
		Buckets: opt.Buckets,
		Limit:   opt.Limit,
	}

	// 從資料庫查詢
	notifications, err := uc.param.Repo.ListLatest(ctx, repoOpt)
	if err != nil {
		return nil, errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: opt.UserID},
				{Key: "buckets", Val: opt.Buckets},
				{Key: "func", Val: "NotificationRepository.ListLatest"},
				{Key: "error", Val: err.Error()},
			},
			"failed to list latest notifications",
		).Wrap(err)
	}

	// 轉換為響應格式
	result := make([]*usecase.UserNotificationResponse, 0, len(notifications))
	for _, n := range notifications {
		result = append(result, uc.entityToUserNotificationResp(n))
	}

	return result, nil
}

// MarkAsRead 標記單個通知為已讀
func (uc *NotificationUseCase) MarkAsRead(ctx context.Context, userID, bucket string, ts string) error {
	// 驗證參數
	if userID == "" || bucket == "" || ts == "" {
		return errs.InputInvalidRangeError("user_id, bucket, and ts are required")
	}

	// 解析 TimeUUID
	timeUUID, err := gocql.ParseUUID(ts)
	if err != nil {
		return errs.InputInvalidRangeError(fmt.Sprintf("invalid ts format: %s", ts)).Wrap(err)
	}

	// 更新資料庫
	if err := uc.param.Repo.MarkRead(ctx, userID, bucket, timeUUID); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: userID},
				{Key: "bucket", Val: bucket},
				{Key: "ts", Val: ts},
				{Key: "func", Val: "NotificationRepository.MarkRead"},
				{Key: "error", Val: err.Error()},
			},
			"failed to mark notification as read",
		).Wrap(err)
	}

	return nil
}

// MarkAllAsRead 標記指定 buckets 範圍內的所有通知為已讀
func (uc *NotificationUseCase) MarkAllAsRead(ctx context.Context, userID string, buckets []string) error {
	// 驗證參數
	if userID == "" {
		return errs.InputInvalidRangeError("user_id is required")
	}

	// 如果未提供 buckets，使用默認的 buckets
	if len(buckets) == 0 {
		buckets = uc.generateDefaultBuckets()
	}

	// 更新資料庫
	if err := uc.param.Repo.MarkAllRead(ctx, userID, buckets); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: userID},
				{Key: "buckets", Val: buckets},
				{Key: "func", Val: "NotificationRepository.MarkAllRead"},
				{Key: "error", Val: err.Error()},
			},
			"failed to mark all notifications as read",
		).Wrap(err)
	}

	return nil
}

// CountUnread 計算未讀通知數量（近似值）
func (uc *NotificationUseCase) CountUnread(ctx context.Context, userID string, buckets []string) (int64, error) {
	// 驗證參數
	if userID == "" {
		return 0, errs.InputInvalidRangeError("user_id is required")
	}

	// 如果未提供 buckets，使用默認的 buckets
	if len(buckets) == 0 {
		buckets = uc.generateDefaultBuckets()
	}

	// 從資料庫查詢
	count, err := uc.param.Repo.CountUnreadApprox(ctx, userID, buckets)
	if err != nil {
		return 0, errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: userID},
				{Key: "buckets", Val: buckets},
				{Key: "func", Val: "NotificationRepository.CountUnreadApprox"},
				{Key: "error", Val: err.Error()},
			},
			"failed to count unread notifications",
		).Wrap(err)
	}

	return count, nil
}

// ==================== CursorUseCase 實現 ====================

// GetCursor 獲取用戶的通知光標
func (uc *NotificationUseCase) GetCursor(ctx context.Context, userID string) (*usecase.NotificationCursor, error) {
	// 驗證參數
	if userID == "" {
		return nil, errs.InputInvalidRangeError("user_id is required")
	}

	// 從資料庫查詢
	cursor, err := uc.param.Repo.GetCursor(ctx, userID)
	if err != nil {
		return nil, errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "user_id", Val: userID},
				{Key: "func", Val: "NotificationRepository.GetCursor"},
				{Key: "error", Val: err.Error()},
			},
			"failed to get notification cursor",
		).Wrap(err)
	}

	// 如果不存在，返回 nil
	if cursor == nil {
		return nil, nil
	}

	// 轉換為響應格式
	return uc.entityToCursor(cursor), nil
}

// UpdateCursor 更新或插入通知光標
func (uc *NotificationUseCase) UpdateCursor(ctx context.Context, param *usecase.UpdateNotificationCursorParam) error {
	// 驗證參數
	if param == nil {
		return errs.InputInvalidRangeError("cursor param is required")
	}
	if param.UID == "" {
		return errs.InputInvalidRangeError("uid is required")
	}
	if param.LastSeenTS == "" {
		return errs.InputInvalidRangeError("last_seen_ts is required")
	}

	// 解析 TimeUUID
	lastSeenTS, err := gocql.ParseUUID(param.LastSeenTS)
	if err != nil {
		return errs.InputInvalidRangeError(fmt.Sprintf("invalid last_seen_ts format: %s", param.LastSeenTS)).Wrap(err)
	}

	// 創建 entity
	cursor := &entity.NotificationCursor{
		UID:        param.UID,
		LastSeenTS: lastSeenTS,
		UpdatedAt:  time.Now(),
	}

	// 更新資料庫
	if err := uc.param.Repo.UpsertCursor(ctx, cursor); err != nil {
		return errs.DBErrorErrorL(
			uc.param.Logger,
			[]errs.LogField{
				{Key: "uid", Val: param.UID},
				{Key: "last_seen_ts", Val: param.LastSeenTS},
				{Key: "func", Val: "NotificationRepository.UpsertCursor"},
				{Key: "error", Val: err.Error()},
			},
			"failed to update notification cursor",
		).Wrap(err)
	}

	return nil
}

// ==================== 輔助函數 ====================

// validateNotificationEvent 驗證通知事件
func (uc *NotificationUseCase) validateNotificationEvent(e *usecase.NotificationEvent) error {
	if e == nil {
		return errs.InputInvalidRangeError("notification event is required")
	}
	if e.EventType == "" {
		return errs.InputInvalidRangeError("event_type is required")
	}
	if e.ActorUID == "" {
		return errs.InputInvalidRangeError("actor_uid is required")
	}
	if e.ObjectType == "" {
		return errs.InputInvalidRangeError("object_type is required")
	}
	if e.ObjectID == "" {
		return errs.InputInvalidRangeError("object_id is required")
	}
	return nil
}

// validateUserNotification 驗證用戶通知
func (uc *NotificationUseCase) validateUserNotification(n *usecase.UserNotification) error {
	if n == nil {
		return errs.InputInvalidRangeError("user notification is required")
	}
	if n.UserID == "" {
		return errs.InputInvalidRangeError("user_id is required")
	}
	if n.EventID == "" {
		return errs.InputInvalidRangeError("event_id is required")
	}
	return nil
}

// parsePriority 解析優先級字符串
func (uc *NotificationUseCase) parsePriority(priorityStr string) (notification.NotifyPriority, error) {
	switch priorityStr {
	case "critical":
		return notification.Critical, nil
	case "high":
		return notification.High, nil
	case "normal":
		return notification.Normal, nil
	case "low":
		return notification.Low, nil
	default:
		return notification.Normal, errors.New("invalid priority value")
	}
}

// generateBucket 生成 bucket 字符串（格式：YYYYMM）
func (uc *NotificationUseCase) generateBucket(t time.Time) string {
	return t.Format("200601")
}

// generateDefaultBuckets 生成默認的 buckets（最近 3 個月）
func (uc *NotificationUseCase) generateDefaultBuckets() []string {
	now := time.Now()
	buckets := make([]string, 0, 3)
	for i := 0; i < 3; i++ {
		month := now.AddDate(0, -i, 0)
		buckets = append(buckets, month.Format("200601"))
	}
	return buckets
}

// calculateDefaultTTL 計算默認 TTL（90 天）
func (uc *NotificationUseCase) calculateDefaultTTL() int {
	return 90 * 24 * 60 * 60 // 90 天，單位：秒
}

// entityToEventResp 將 entity 轉換為 EventResp
func (uc *NotificationUseCase) entityToEventResp(e *entity.NotificationEvent) *usecase.NotificationEventResp {
	return &usecase.NotificationEventResp{
		EventID:    e.EventID.String(),
		EventType:  e.EventType,
		ActorUID:   e.ActorUID,
		ObjectType: e.ObjectType,
		ObjectID:   e.ObjectID,
		Title:      e.Title,
		Body:       e.Body,
		Payload:    e.Payload,
		Priority:   e.Priority.ToString(),
		CreatedAt:  e.CreatedAt.UTC().Format(time.RFC3339),
	}
}

// entityToEvent 將 entity 轉換為 Event
func (uc *NotificationUseCase) entityToEvent(e *entity.NotificationEvent) *usecase.NotificationEventResp {
	return &usecase.NotificationEventResp{
		EventID:    e.EventID.String(),
		EventType:  e.EventType,
		ActorUID:   e.ActorUID,
		ObjectType: e.ObjectType,
		ObjectID:   e.ObjectID,
		Title:      e.Title,
		Body:       e.Body,
		Payload:    e.Payload,
		Priority:   e.Priority.ToString(),
		CreatedAt:  e.CreatedAt.UTC().Format(time.RFC3339),
	}
}

// entityToUserNotificationResp 將 entity 轉換為 UserNotificationResponse
func (uc *NotificationUseCase) entityToUserNotificationResp(n *entity.UserNotification) *usecase.UserNotificationResponse {
	resp := &usecase.UserNotificationResponse{
		UserID:  n.UserID,
		Bucket:  n.Bucket,
		TS:      n.TS.String(),
		EventID: n.EventID.String(),
		Status:  n.Status.ToString(),
	}

	// 如果 ReadAt 不是零值，設置為字符串
	if !n.ReadAt.IsZero() {
		readAtStr := n.ReadAt.UTC().Format(time.RFC3339)
		resp.ReadAt = &readAtStr
	}

	return resp
}

// entityToCursor 將 entity 轉換為 Cursor
func (uc *NotificationUseCase) entityToCursor(c *entity.NotificationCursor) *usecase.NotificationCursor {
	return &usecase.NotificationCursor{
		UID:        c.UID,
		LastSeenTS: c.LastSeenTS.String(),
		UpdatedAt:  c.UpdatedAt.UTC().Format(time.RFC3339),
	}
}
