package postgres

import (
	"context"
	"fmt"
	"time"

	"github.com/bytedance/sonic"
	"github.com/redis/go-redis/v9"
	"gorm.io/datatypes"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

// ExportTaskStore 导出主任务数据访问层。
type ExportTaskStore struct {
	db    *gorm.DB
	redis *redis.Client
}

// NewExportTaskStore 创建导出主任务 Store。
func NewExportTaskStore(db *gorm.DB, redis *redis.Client) *ExportTaskStore {
	return &ExportTaskStore{db: db, redis: redis}
}

// Create 创建导出主任务。
func (s *ExportTaskStore) Create(ctx context.Context, task *model.ExportTask) error {
	return s.db.WithContext(ctx).Create(task).Error
}

// GetByID 按 ID 查询任务（带数据权限）。
func (s *ExportTaskStore) GetByID(ctx context.Context, id uint) (*model.ExportTask, error) {
	var task model.ExportTask
	query := applyExportTaskVisibility(ctx, s.db.WithContext(ctx).Model(&model.ExportTask{}))
	if err := query.Where("id = ?", id).First(&task).Error; err != nil {
		return nil, err
	}
	return &task, nil
}

// GetByIDForWorker 按 ID 查询任务（Worker 使用，不带数据权限）。
func (s *ExportTaskStore) GetByIDForWorker(ctx context.Context, id uint) (*model.ExportTask, error) {
	var task model.ExportTask
	if err := s.db.WithContext(ctx).Where("id = ?", id).First(&task).Error; err != nil {
		return nil, err
	}
	return &task, nil
}

// List 查询导出任务列表（带数据权限）。
func (s *ExportTaskStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]any) ([]*model.ExportTask, int64, error) {
	var (
		items []*model.ExportTask
		total int64
	)

	query := applyExportTaskVisibility(ctx, s.db.WithContext(ctx).Model(&model.ExportTask{}))

	if scene, ok := filters["scene"].(string); ok && scene != "" {
		query = query.Where("scene = ?", scene)
	}
	if status, ok := filters["status"].(int); ok && status > 0 {
		query = query.Where("status = ?", status)
	}
	if startTime, ok := filters["start_time"].(time.Time); ok && !startTime.IsZero() {
		query = query.Where("created_at >= ?", startTime)
	}
	if endTime, ok := filters["end_time"].(time.Time); ok && !endTime.IsZero() {
		query = query.Where("created_at <= ?", endTime)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if opts == nil {
		opts = &store.QueryOptions{Page: 1, PageSize: constants.DefaultPageSize}
	}
	if opts.Page <= 0 {
		opts.Page = 1
	}
	if opts.PageSize <= 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	offset := (opts.Page - 1) * opts.PageSize
	query = query.Offset(offset).Limit(opts.PageSize)

	if opts.OrderBy != "" {
		query = query.Order(opts.OrderBy)
	} else {
		query = query.Order("created_at DESC")
	}

	if err := query.Find(&items).Error; err != nil {
		return nil, 0, err
	}

	return items, total, nil
}

// GenerateTaskNo 生成导出任务编号。
func (s *ExportTaskStore) GenerateTaskNo() string {
	now := time.Now()
	return fmt.Sprintf("EXP-%s-%06d", now.Format("20060102"), now.UnixNano()%1000000)
}

// TryMarkProcessingIfPending 尝试将任务从待处理更新为处理中。
func (s *ExportTaskStore) TryMarkProcessingIfPending(ctx context.Context, taskID uint, updater uint) (bool, error) {
	now := time.Now()
	result := s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status = ?", taskID, constants.ExportTaskStatusPending).
		Updates(map[string]any{
			"status":     constants.ExportTaskStatusProcessing,
			"started_at": now,
			"updated_at": now,
			"updater":    updater,
		})
	return result.RowsAffected > 0, result.Error
}

// UpdateDispatchPlan 更新 dispatch 阶段计算出的总量信息。
func (s *ExportTaskStore) UpdateDispatchPlan(ctx context.Context, taskID uint, totalRows, totalShards int, updater uint) error {
	now := time.Now()
	return s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ?", taskID).
		Updates(map[string]any{
			"total_rows":    totalRows,
			"total_shards":  totalShards,
			"updated_at":    now,
			"updater":       updater,
			"error_message": "",
		}).Error
}

// UpdateResolvedHeaders 固定导出任务运行期表头。
func (s *ExportTaskStore) UpdateResolvedHeaders(ctx context.Context, taskID uint, headers []string, updater uint) error {
	var task model.ExportTask
	if err := s.db.WithContext(ctx).
		Select("id", "query_json").
		Where("id = ?", taskID).
		First(&task).Error; err != nil {
		return err
	}

	raw := make(map[string]any)
	if len(task.QueryJSON) > 0 {
		if err := sonic.Unmarshal(task.QueryJSON, &raw); err != nil {
			return err
		}
	}
	raw["resolved_headers"] = headers

	bytes, err := sonic.Marshal(raw)
	if err != nil {
		return err
	}

	now := time.Now()
	return s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ?", taskID).
		Updates(map[string]any{
			"query_json": datatypes.JSON(bytes),
			"updated_at": now,
			"updater":    updater,
		}).Error
}

// IsCancelRequested 查询是否已请求取消。
func (s *ExportTaskStore) IsCancelRequested(ctx context.Context, taskID uint) (bool, error) {
	var value bool
	if err := s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ?", taskID).
		Pluck("cancel_requested", &value).Error; err != nil {
		return false, err
	}
	return value, nil
}

// CancelPendingTask 将待处理任务直接取消。
func (s *ExportTaskStore) CancelPendingTask(ctx context.Context, taskID uint, updater uint) (bool, error) {
	now := time.Now()
	result := s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status = ?", taskID, constants.ExportTaskStatusPending).
		Updates(map[string]any{
			"status":           constants.ExportTaskStatusCancelled,
			"cancel_requested": true,
			"progress":         100,
			"completed_at":     now,
			"updated_at":       now,
			"updater":          updater,
		})
	return result.RowsAffected > 0, result.Error
}

// SetCancelRequested 标记处理中任务取消请求。
func (s *ExportTaskStore) SetCancelRequested(ctx context.Context, taskID uint, updater uint) (bool, error) {
	now := time.Now()
	result := s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status = ?", taskID, constants.ExportTaskStatusProcessing).
		Updates(map[string]any{
			"cancel_requested": true,
			"updated_at":       now,
			"updater":          updater,
		})
	return result.RowsAffected > 0, result.Error
}

// MarkCancelledByWorker 在 Worker 侧将任务状态更新为已取消。
func (s *ExportTaskStore) MarkCancelledByWorker(ctx context.Context, taskID uint, updater uint, msg string) error {
	now := time.Now()
	return s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status IN ?", taskID, []int{constants.ExportTaskStatusPending, constants.ExportTaskStatusProcessing}).
		Updates(map[string]any{
			"status":           constants.ExportTaskStatusCancelled,
			"cancel_requested": true,
			"progress":         100,
			"completed_at":     now,
			"updated_at":       now,
			"updater":          updater,
			"error_message":    msg,
		}).Error
}

// MarkCompleted 将任务更新为已完成。
func (s *ExportTaskStore) MarkCompleted(ctx context.Context, taskID uint, updater uint, fileKey string, fileSize int64) error {
	now := time.Now()
	return s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status = ?", taskID, constants.ExportTaskStatusProcessing).
		Updates(map[string]any{
			"status":        constants.ExportTaskStatusCompleted,
			"progress":      100,
			"file_key":      fileKey,
			"file_size":     fileSize,
			"completed_at":  now,
			"updated_at":    now,
			"updater":       updater,
			"error_message": "",
		}).Error
}

// MarkFailed 将任务更新为失败。
func (s *ExportTaskStore) MarkFailed(ctx context.Context, taskID uint, updater uint, errorMessage string) error {
	now := time.Now()
	return s.db.WithContext(ctx).
		Model(&model.ExportTask{}).
		Where("id = ? AND status IN ?", taskID, []int{constants.ExportTaskStatusPending, constants.ExportTaskStatusProcessing}).
		Updates(map[string]any{
			"status":        constants.ExportTaskStatusFailed,
			"completed_at":  now,
			"updated_at":    now,
			"updater":       updater,
			"error_message": errorMessage,
		}).Error
}

// ApplyShardSuccess 在分片成功后更新主任务进度与统计。
func (s *ExportTaskStore) ApplyShardSuccess(ctx context.Context, taskID uint, updater uint, rowCount int) error {
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var task model.ExportTask
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("id = ?", taskID).
			First(&task).Error; err != nil {
			return err
		}
		if task.Status != constants.ExportTaskStatusProcessing {
			return nil
		}

		processedRows := task.ProcessedRows + rowCount
		successShards := task.SuccessShards + 1
		progress := task.Progress

		if task.TotalRows > 0 {
			progress = processedRows * 100 / task.TotalRows
		} else if task.TotalShards > 0 {
			progress = successShards * 100 / task.TotalShards
		}
		if progress > 99 {
			progress = 99
		}
		if progress < task.Progress {
			progress = task.Progress
		}

		now := time.Now()
		return tx.Model(&model.ExportTask{}).
			Where("id = ?", taskID).
			Updates(map[string]any{
				"processed_rows": processedRows,
				"success_shards": successShards,
				"progress":       progress,
				"updated_at":     now,
				"updater":        updater,
			}).Error
	})
}

// ApplyShardFailed 在分片失败后更新主任务失败计数。
func (s *ExportTaskStore) ApplyShardFailed(ctx context.Context, taskID uint, updater uint, errorMessage string) error {
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var task model.ExportTask
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("id = ?", taskID).
			First(&task).Error; err != nil {
			return err
		}
		if task.Status != constants.ExportTaskStatusProcessing {
			return nil
		}

		now := time.Now()
		updates := map[string]any{
			"failed_shards": task.FailedShards + 1,
			"updated_at":    now,
			"updater":       updater,
		}
		if task.ErrorMessage == "" {
			updates["error_message"] = errorMessage
		}

		return tx.Model(&model.ExportTask{}).
			Where("id = ?", taskID).
			Updates(updates).Error
	})
}

func applyExportTaskVisibility(ctx context.Context, query *gorm.DB) *gorm.DB {
	userType := middleware.GetUserTypeFromContext(ctx)

	switch userType {
	case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
		return query
	case constants.UserTypeAgent:
		shopIDs := middleware.GetSubordinateShopIDs(ctx)
		if shopIDs == nil {
			shopID := middleware.GetShopIDFromContext(ctx)
			if shopID == 0 {
				return query.Where("1 = 0")
			}
			return query.Where("creator_shop_id = ?", shopID)
		}
		if len(shopIDs) == 0 {
			return query.Where("1 = 0")
		}
		return query.Where("creator_shop_id IN ?", shopIDs)
	case constants.UserTypeEnterprise:
		enterpriseID := middleware.GetEnterpriseIDFromContext(ctx)
		if enterpriseID == 0 {
			return query.Where("1 = 0")
		}
		return query.Where("creator_enterprise_id = ?", enterpriseID)
	default:
		userID := middleware.GetUserIDFromContext(ctx)
		if userID == 0 {
			return query.Where("1 = 0")
		}
		return query.Where("creator_user_id = ?", userID)
	}
}
