// Package cell 提供单房间 Room Cell 执行单元，负责把同一房间的状态变更串行化。
package cell

import (
	"context"

	"hyapp/services/room-service/internal/room/rank"
	"hyapp/services/room-service/internal/room/state"
)

// taskResult 是 mailbox 内同步任务的返回值容器。
type taskResult struct {
	// value 承载任务返回的业务结果，调用方按约定做类型断言。
	value any
	// err 承载任务执行失败原因，失败时 Room Cell 不应该替换当前状态。
	err error
}

// task 表示一个需要在 Room Cell 内串行执行的闭包。
type task struct {
	// run 接收当前房间状态和本地榜单，所有房间内写操作都必须在该闭包里完成。
	run func(*state.RoomState, *rank.LocalRank) (any, error)
	// resp 是同步等待结果的单次响应通道。
	resp chan taskResult
}

// RoomCell 用 mailbox 串行化单房间内的全部状态变更。
type RoomCell struct {
	// roomID 是该 Cell 唯一负责的房间标识。
	roomID string
	// state 是房间核心业务状态，只能由 loop goroutine 访问和替换。
	state *state.RoomState
	// rank 是房间内本地礼物榜，和 state 同线程更新，避免榜单与房间热度错位。
	rank *rank.LocalRank
	// mailbox 是单房间命令队列，所有任务按发送顺序执行。
	mailbox chan task
}

// New 创建一个新的单房间执行单元。
func New(initialState *state.RoomState, initialRank *rank.LocalRank) *RoomCell {
	// 初始化时要求调用方已经完成恢复或创建，RoomCell 不直接访问 repository。
	c := &RoomCell{
		roomID:  initialState.RoomID,
		state:   initialState,
		rank:    initialRank,
		mailbox: make(chan task),
	}

	// 每个房间一个 goroutine 串行处理任务，避免房间内状态用大锁到处包裹。
	go c.loop()

	return c
}

// Do 把一个同步任务提交到 mailbox，并等待串行执行结果。
func (c *RoomCell) Do(ctx context.Context, run func(*state.RoomState, *rank.LocalRank) (any, error)) (any, error) {
	// 响应通道带 1 个缓冲，避免调用方 ctx 超时后 loop goroutine 卡在回写结果上。
	resp := make(chan taskResult, 1)
	req := task{
		run:  run,
		resp: resp,
	}

	select {
	case <-ctx.Done():
		// 命令还没进入 mailbox 前超时，不会对房间状态产生任何影响。
		return nil, ctx.Err()
	case c.mailbox <- req:
	}

	select {
	case <-ctx.Done():
		// 命令可能已经在 Cell 内执行完成；调用方超时只影响本次等待，不回滚已提交状态。
		return nil, ctx.Err()
	case result := <-resp:
		return result.value, result.err
	}
}

// Snapshot 返回当前房间态和榜单的独立副本。
func (c *RoomCell) Snapshot(ctx context.Context) (*state.RoomState, *rank.LocalRank, error) {
	// 快照也走 mailbox，保证读取到的是某个命令边界上的一致状态。
	value, err := c.Do(ctx, func(current *state.RoomState, currentRank *rank.LocalRank) (any, error) {
		return struct {
			state *state.RoomState
			rank  *rank.LocalRank
		}{
			state: current.Clone(),
			rank:  currentRank.Clone(),
		}, nil
	})
	if err != nil {
		return nil, nil, err
	}

	// Do 返回的是内部匿名结构，类型断言只在本函数私有闭包和读取点之间使用。
	result := value.(struct {
		state *state.RoomState
		rank  *rank.LocalRank
	})

	return result.state, result.rank, nil
}

func (c *RoomCell) loop() {
	for task := range c.mailbox {
		// loop 是 state/rank 的唯一写入者，任务内部不需要再加状态锁。
		value, err := task.run(c.state, c.rank)
		task.resp <- taskResult{
			value: value,
			err:   err,
		}
	}
}
