package com.alterdekim.hearthhack.component.processor;

import com.alterdekim.Protocol;
import com.alterdekim.hearthhack.component.TcpConnection;
import com.alterdekim.hearthhack.component.interfaces.OnRoomFullCallback;
import com.alterdekim.hearthhack.dto.RoomDTO;
import com.alterdekim.hearthhack.util.BattleNetPacket;
import com.alterdekim.hearthhack.util.GameType;
import com.google.protobuf.ByteString;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.UUID;

import static com.alterdekim.hearthhack.component.GamePool.PLAYERS_COUNT;

@Slf4j
public class GameMasterProcessor extends Processor {

    public GameMasterProcessor() {
        this.setProcessorId(8);
    }

    private void processFindGame(BattleNetPacket packet, TcpConnection conn) throws Exception {
        // FindGameRequest FindGameResponse game_master
        // GameType
        Protocol.FindGameRequest gameRequest = Protocol.FindGameRequest.parseFrom(packet.getBody());
        //log.info("FindGameRequest: {}", gameRequest);
        Protocol.Attribute a = gameRequest.getProperties().getCreationAttributesList().stream()
                .filter(p -> p.hasName() && p.getName().equals("type"))
                .findFirst()
                .get();
        GameType gameType = GameType.parseFromInt((int) a.getValue().getIntValue());
        if( gameType != GameType.GT_CASUAL ) return;
        Protocol.FindGameResponse response = Protocol.FindGameResponse.newBuilder()
                .setQueued(true)
                .build();

        Protocol.Header header = Processor.generateResponse(response.getSerializedSize(), packet.getHeader().getToken(), 0, 0);
        conn.send(new BattleNetPacket(header, response.toByteArray()));


        Protocol.GameFoundNotification notification = Protocol.GameFoundNotification.newBuilder()
                .setRequestId(gameRequest.getRequestId())
                //.setGameHandle(Protocol.GameHandle.newBuilder().setFactoryId(gameRequest.getFactoryId()))
                .addConnectInfo(Protocol.ConnectInfo.newBuilder()
                        .setHost("localhost")
                        .setPort(1119)
                        .setMemberId(Protocol.EntityId.newBuilder()
                                .setHigh(72057594037927936L)
                                .setLow(437154195L))
                ).build();

        // method_id = 1
        // service = 3

        header = Protocol.Header.newBuilder()
                .setServiceId(3)
                .setMethodId(1)
                .setSize(notification.getSerializedSize())
                .setToken(conn.nextToken())
                .setObjectId(0)
                .setStatus(0)
                .build();

        conn.send(new BattleNetPacket(header, notification.toByteArray()));


        final String uuid = UUID.randomUUID().toString();

        Long roomId = conn.getRoomService().findRoomWithPlayer().orElse(conn.getRoomService().createRoom(new RoomDTO(uuid))); // TODO: make 1 player rooms check.
        log.info("Got roomID (join/create): {}", roomId);
        final String pwd = conn.getRoomService().findById(roomId).orElse(new RoomDTO(uuid)).getRoomPassword();
        log.info("getRoomService.joinRoom; {}", gameRequest);
        conn.getRoomService().joinRoom(roomId, conn.getUserId(),
                gameRequest.getPlayerList().get(0)
                        .getAttributeList()
                        .stream()
                        .filter(p -> p.hasName() && p.getName().equals("deck"))
                        .findFirst()
                        .get()
                        .getValue()
                        .getIntValue()
        ); // TODO: make 1 player rooms check.
        // TODO: check there for GameRoom error (userId and deckId the same)
        conn.getGamePool().getCallbacks().put(conn.getUserId(), new OnRoomFullCallback() {
            @Override
            public void onRoomFull() {
                try {
                    Protocol.Notification n1 = Protocol.Notification.newBuilder()
                            .setType("G_RESULT")
                            .setSenderId(Protocol.EntityId.newBuilder()
                                    .setLow(0)
                                    .setHigh(144115188075855872L))
                            .setTargetId(Protocol.EntityId.newBuilder()
                                    .setLow(78330215L)
                                    .setHigh(144115198130930503L))
                            .addAttribute(Protocol.Attribute.newBuilder()
                                    .setName("game_request_id")
                                    .setValue(
                                            Protocol.Variant.newBuilder()
                                                    .setUintValue(1338541484650062879L * 10L)
                                    )
                            )
                            .addAttribute(Protocol.Attribute.newBuilder()
                                    .setName("game_handle")
                                    .setValue(
                                            Protocol.Variant.newBuilder()
                                                    .setMessageValue(
                                                            Protocol.GameHandle.newBuilder()
                                                                    .setFactoryId((143637261465044426L * 100L) + 14L)
                                                                    .setGameId(Protocol.EntityId.newBuilder()
                                                                            .setHigh(432345578572981998L)
                                                                            .setLow((123678367967794400L * 100L) + 12L))
                                                                    .build()
                                                                    .toByteString()
                                                    )
                                    )
                            )
                            .addAttribute(Protocol.Attribute.newBuilder()
                                    .setName("connection_info")
                                    .setValue(
                                            Protocol.Variant.newBuilder()
                                                    .setMessageValue(
                                                            Protocol.ConnectInfo.newBuilder()
                                                                    .setMemberId(Protocol.EntityId.newBuilder()
                                                                            .setHigh(144115198130930503L)
                                                                            .setLow(78330215L)
                                                                    )
                                                                    .setHost("192.168.0.10")
                                                                    .setPort(3724)
                                                                    .setToken(ByteString.copyFromUtf8(pwd)) // password
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("version")
                                                                            .setValue(Protocol.Variant.newBuilder().setStringValue("eiK2qv")))
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("game")
                                                                            .setValue(Protocol.Variant.newBuilder().setIntValue(roomId))) // game_id
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("player")
                                                                            .setValue(Protocol.Variant.newBuilder().setIntValue(1L)))
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("id")
                                                                            .setValue(Protocol.Variant.newBuilder().setIntValue(conn.getUserId())))
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("resumable")
                                                                            .setValue(Protocol.Variant.newBuilder().setBoolValue(true)))
                                                                    .addAttribute(Protocol.Attribute
                                                                            .newBuilder()
                                                                            .setName("spectator_password")
                                                                            .setValue(Protocol.Variant.newBuilder().setStringValue("oDipIf")))
                                                                    .build().toByteString()
                                                    )
                                    )
                            )
                            .addAttribute(Protocol.Attribute.newBuilder()
                                    .setName("error")
                                    .setValue(
                                            Protocol.Variant.newBuilder()
                                                    .setUintValue(0)
                                    )
                            ).build();

                    Protocol.Header header = Protocol.Header.newBuilder()
                            .setServiceId(4)
                            .setMethodId(1)
                            .setSize(n1.getSerializedSize())
                            .setToken(conn.nextToken())
                            .setObjectId(0)
                            .setStatus(0)
                            .build();

                    conn.send(new BattleNetPacket(header, n1.toByteArray()));
                } catch (Exception e) {
                    log.error("GameMasterCallback: {}", e.getMessage());
                }
            }
        });
    }

    @Override
    public void process(BattleNetPacket packet, TcpConnection conn) throws Exception {
        switch(packet.getHeader().getMethodId()) {
            case 3 -> processFindGame(packet, conn);
            default -> log.info("Weird method id: {}", packet.getHeader().getMethodId());
        }
    }

    @Override
    public String getExportName() {
        return "bnet.protocol.channel_invitation.ChannelInvitationService";
    }
}
