package service

import (
	"context"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc/codes"
	"permissionservice/api/authservice"
	"permissionservice/api/noteservice"
	"permissionservice/api/permissionservice"
	"permissionservice/internal/model"
	"permissionservice/internal/repository"
	"permissionservice/internal/tools"
)

type PermissionService struct {
	AuthClient           authservice.AuthServiceClient
	NoteClient           noteservice.NoteServiceClient
	PermissionRepository *repository.PermissionRepository
}

func (p PermissionService) GetNotes(context context.Context, request *permissionservice.Request) (*noteservice.NoteResponse, error) {
	data, err := proto.Marshal(request)
	if err != nil {
		return nil, tools.ConvertError(codes.FailedPrecondition, "Can`t encode request", err)
	}
	userId, err := p.VerifyCredentials(context, data)

	if err != nil {
		return nil, err
	}

	return p.GetUserNotes(context, userId)
}

func (p PermissionService) GetUserNotes(context context.Context, userId string) (*noteservice.NoteResponse, error) {
	permissions, err := p.PermissionRepository.GetAllNotePermissions(context, userId)
	if err != nil {
		return nil, err
	}

	var noteIds []string
	for _, value := range permissions {
		noteIds = append(noteIds, value.NoteId)
	}

	notes, err := p.NoteClient.GetNotes(context, &noteservice.NoteRequest{
		Ids: noteIds,
	})
	if err != nil {
		return nil, err
	}

	return notes, nil
}

func (p PermissionService) AddOrUpdateNote(context context.Context, request *noteservice.NoteOperationRequest) (*noteservice.NoteResponse, error) {

	data, err := proto.Marshal(request)
	if err != nil {
		return nil, tools.ConvertError(codes.FailedPrecondition, "Can`t encode request", err)
	}

	userId, err := p.VerifyCredentials(context, data)

	if err != nil {
		return nil, err
	}
	if request.Note.Id == "" {
		return p.CreateNote(context, request, userId)
	}
	_, err = p.PermissionRepository.CheckIfUserHavePermissions(context, userId, request.Note.Id, &model.Permissions{
		Read:   true,
		Write:  true,
		Delete: false,
		Share:  false,
		Owner:  false,
	})
	if err != nil {
		return nil, err
	}
	return p.UpdateNote(context, request, userId)
}

func (p PermissionService) CreateNote(context context.Context, request *noteservice.NoteOperationRequest, userId string) (*noteservice.NoteResponse, error) {
	note, err := p.NoteClient.CreateNote(context, request)
	if err != nil {
		return nil, err
	}
	err = p.PermissionRepository.CreateNotePermission(context, userId, note.Id, &model.Permissions{
		Read:   true,
		Write:  true,
		Delete: true,
		Share:  true,
		Owner:  true,
	})
	if err != nil {
		return nil, err
	}

	return p.GetUserNotes(context, userId)
}

func (p PermissionService) UpdateNote(context context.Context, request *noteservice.NoteOperationRequest, userId string) (*noteservice.NoteResponse, error) {
	_, err := p.NoteClient.UpdateNote(context, request)
	if err != nil {
		return nil, err
	}

	return p.GetUserNotes(context, userId)
}

func (p PermissionService) DeleteNote(context context.Context, request *noteservice.DeleteRequest) (*noteservice.NoteResponse, error) {
	data, err := proto.Marshal(request)
	if err != nil {
		return nil, tools.ConvertError(codes.FailedPrecondition, "Can`t encode request", err)
	}
	userId, err := p.VerifyCredentials(context, data)
	if err != nil {
		return nil, err
	}
	notePermission, err := p.PermissionRepository.CheckIfUserHavePermissions(context, userId, request.Id, &model.Permissions{
		Read:   true,
		Write:  false,
		Delete: true,
		Share:  false,
		Owner:  false,
	})
	if err != nil {
		return nil, err
	}
	_, err = p.NoteClient.DeleteNote(context, &noteservice.DeleteRequest{
		Id: request.Id,
	})
	if err != nil {
		return nil, err
	}

	err = p.PermissionRepository.DeleteNotePermissions(context, notePermission.Id)
	if err != nil {
		return nil, err
	}
	return p.GetUserNotes(context, userId)
}

func (p PermissionService) ShareNote(context context.Context, request *permissionservice.ShareRequest) (*permissionservice.Request, error) {
	data, err := proto.Marshal(request)
	if err != nil {
		return nil, tools.ConvertError(codes.FailedPrecondition, "Can`t encode request", err)
	}
	userId, err := p.VerifyCredentials(context, data)
	if err != nil {
		return nil, err
	}
	_, err = p.PermissionRepository.CheckIfUserHavePermissions(context, userId, request.NoteId, &model.Permissions{
		Read:   true,
		Write:  false,
		Delete: false,
		Share:  true,
		Owner:  false,
	})
	if err != nil {
		return nil, err
	}

	user, err := p.AuthClient.FindUser(context, &authservice.FindUserRequest{
		Email: request.Email,
	})

	if err != nil {
		return nil, err
	}

	err = p.PermissionRepository.CreateNotePermission(context, user.UserId, request.NoteId, model.ConvertToServicePermissions(request.Permissions))
	if err != nil {
		return nil, err
	}

	return &permissionservice.Request{}, nil
}

func (p PermissionService) SubscribeNotes(*permissionservice.Request, permissionservice.PermissionService_SubscribeNotesServer) error {
	panic("implement me")
}

func (p PermissionService) VerifyCredentials(context context.Context, request []byte) (string, error) {
	ctx, err := tools.ProxyMedataFromContext(context, request)
	if err != nil {
		return "", err
	}
	verifyResponse, err := p.AuthClient.Verify(ctx, &authservice.Request{})
	if err != nil {
		return "", err
	}

	if !verifyResponse.Valid {
		return "", tools.CreateError(codes.Unauthenticated, "Token is not valid", "Invalid token")
	}
	return verifyResponse.UserId, nil
}
