package grpc

import (
	"context"

	robotv1 "hyapp.local/api/proto/robot/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	robotdomain "hyapp/services/robot-service/internal/domain/robot"
	"hyapp/services/robot-service/internal/service/gamerobot"
	"hyapp/services/robot-service/internal/service/roomrobot"
)

// Server 把 game-robot 和 room-robot 两个业务域暴露成同一个 robot-service gRPC 进程。
type Server struct {
	robotv1.UnimplementedGameRobotServiceServer
	robotv1.UnimplementedRoomRobotServiceServer

	game *gamerobot.Service
	room *roomrobot.Service
}

func NewServer(game *gamerobot.Service, room *roomrobot.Service) *Server {
	return &Server{game: game, room: room}
}

func (s *Server) ListGameRobots(ctx context.Context, req *robotv1.ListGameRobotsRequest) (*robotv1.ListGameRobotsResponse, error) {
	if s == nil || s.game == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "game robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	items, next, serverTimeMS, err := s.game.ListRobots(ctx, req.GetMeta().GetAppCode(), req.GetGameId(), req.GetStatus(), req.GetPageSize(), req.GetCursor())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &robotv1.ListGameRobotsResponse{NextCursor: next, ServerTimeMs: serverTimeMS, Robots: make([]*robotv1.Robot, 0, len(items))}
	for _, item := range items {
		resp.Robots = append(resp.Robots, robotToProto(item))
	}
	return resp, nil
}

func (s *Server) RegisterGameRobots(ctx context.Context, req *robotv1.RegisterGameRobotsRequest) (*robotv1.RegisterGameRobotsResponse, error) {
	if s == nil || s.game == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "game robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	items, serverTimeMS, err := s.game.RegisterRobots(ctx, req.GetMeta().GetAppCode(), req.GetGameId(), req.GetUserIds(), req.GetMeta().GetActorUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &robotv1.RegisterGameRobotsResponse{ServerTimeMs: serverTimeMS, Robots: make([]*robotv1.Robot, 0, len(items))}
	for _, item := range items {
		resp.Robots = append(resp.Robots, robotToProto(item))
	}
	return resp, nil
}

func (s *Server) PickGameRobot(ctx context.Context, req *robotv1.PickGameRobotRequest) (*robotv1.GameRobotResponse, error) {
	if s == nil || s.game == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "game robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	item, serverTimeMS, err := s.game.PickRobot(ctx, req.GetMeta().GetAppCode(), req.GetGameId(), req.GetMatchId(), req.GetExcludeUserIds(), req.GetSelectedAtMs())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.GameRobotResponse{Robot: robotToProto(item), ServerTimeMs: serverTimeMS}, nil
}

func (s *Server) SetGameRobotStatus(ctx context.Context, req *robotv1.SetGameRobotStatusRequest) (*robotv1.GameRobotResponse, error) {
	if s == nil || s.game == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "game robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	item, serverTimeMS, err := s.game.SetRobotStatus(ctx, req.GetMeta().GetAppCode(), req.GetGameId(), req.GetUserId(), req.GetStatus())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.GameRobotResponse{Robot: robotToProto(item), ServerTimeMs: serverTimeMS}, nil
}

func (s *Server) DeleteGameRobot(ctx context.Context, req *robotv1.DeleteGameRobotRequest) (*robotv1.DeleteGameRobotResponse, error) {
	if s == nil || s.game == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "game robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	serverTimeMS, err := s.game.DeleteRobot(ctx, req.GetMeta().GetAppCode(), req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.DeleteGameRobotResponse{Deleted: true, ServerTimeMs: serverTimeMS}, nil
}

func (s *Server) ListRoomRobots(ctx context.Context, req *robotv1.ListRoomRobotsRequest) (*robotv1.ListRoomRobotsResponse, error) {
	if s == nil || s.room == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	items, next, serverTimeMS, err := s.room.ListRobots(ctx, req.GetMeta().GetAppCode(), req.GetRoomScene(), req.GetStatus(), req.GetPageSize(), req.GetCursor())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &robotv1.ListRoomRobotsResponse{NextCursor: next, ServerTimeMs: serverTimeMS, Robots: make([]*robotv1.Robot, 0, len(items))}
	for _, item := range items {
		resp.Robots = append(resp.Robots, robotToProto(item))
	}
	return resp, nil
}

func (s *Server) RegisterRoomRobots(ctx context.Context, req *robotv1.RegisterRoomRobotsRequest) (*robotv1.RegisterRoomRobotsResponse, error) {
	if s == nil || s.room == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	items, serverTimeMS, err := s.room.RegisterRobots(ctx, req.GetMeta().GetAppCode(), req.GetRoomScene(), req.GetUserIds(), req.GetMeta().GetActorUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &robotv1.RegisterRoomRobotsResponse{ServerTimeMs: serverTimeMS, Robots: make([]*robotv1.Robot, 0, len(items))}
	for _, item := range items {
		resp.Robots = append(resp.Robots, robotToProto(item))
	}
	return resp, nil
}

func (s *Server) PickRoomRobot(ctx context.Context, req *robotv1.PickRoomRobotRequest) (*robotv1.RoomRobotResponse, error) {
	if s == nil || s.room == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	item, serverTimeMS, err := s.room.PickRobot(ctx, req.GetMeta().GetAppCode(), req.GetRoomScene(), req.GetRoomId(), req.GetSelectedAtMs())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.RoomRobotResponse{Robot: robotToProto(item), ServerTimeMs: serverTimeMS}, nil
}

func (s *Server) SetRoomRobotStatus(ctx context.Context, req *robotv1.SetRoomRobotStatusRequest) (*robotv1.RoomRobotResponse, error) {
	if s == nil || s.room == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	item, serverTimeMS, err := s.room.SetRobotStatus(ctx, req.GetMeta().GetAppCode(), req.GetRoomScene(), req.GetUserId(), req.GetStatus())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.RoomRobotResponse{Robot: robotToProto(item), ServerTimeMs: serverTimeMS}, nil
}

func (s *Server) DeleteRoomRobot(ctx context.Context, req *robotv1.DeleteRoomRobotRequest) (*robotv1.DeleteRoomRobotResponse, error) {
	if s == nil || s.room == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room robot service is not configured"))
	}
	ctx = contextWithMeta(ctx, req.GetMeta())
	serverTimeMS, err := s.room.DeleteRobot(ctx, req.GetMeta().GetAppCode(), req.GetRoomScene(), req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &robotv1.DeleteRoomRobotResponse{Deleted: true, ServerTimeMs: serverTimeMS}, nil
}

func contextWithMeta(ctx context.Context, meta *robotv1.RequestMeta) context.Context {
	if meta == nil {
		return ctx
	}
	return appcode.WithContext(ctx, meta.GetAppCode())
}

func robotToProto(item robotdomain.Robot) *robotv1.Robot {
	return &robotv1.Robot{
		AppCode:          item.AppCode,
		RobotScope:       item.Scope,
		GameId:           item.GameID,
		RoomScene:        item.RoomScene,
		UserId:           item.UserID,
		Status:           item.Status,
		CreatedByAdminId: item.CreatedByAdminID,
		CreatedAtMs:      item.CreatedAtMS,
		UpdatedAtMs:      item.UpdatedAtMS,
		LastUsedAtMs:     item.LastUsedAtMS,
		UsedCount:        item.UsedCount,
	}
}
