package todo

import (
	"cmp"
	"context"
	"net/http"
	"slices"
	"time"

	"connectrpc.com/connect"
	"connectrpc.com/validate"
	"github.com/<@var(context.project.name)>/<@var(context.project.name)>/db"
	todov1 "<@var(context.project.goprefix)>/<@var(context.project.name)>/gen/todo/v1"
	"<@var(context.project.goprefix)>/<@var(context.project.name)>/gen/todo/v1/todov1connect"
	. "<@var(context.project.goprefix)>/glstack-test/utils"
	"github.com/jackc/pgx/v5/pgtype"
)

type TodoServer struct{}

func (srv *TodoServer) CreateTodo(ctx context.Context, req *connect.Request[todov1.CreateTodoRequest]) (*connect.Response[todov1.CreateTodoResponse], error) {
	var id pgtype.UUID
	err := id.Scan(*req.Msg.Todo.Id)
	if err != nil {
		return nil, err
	}
	todo, err := db.Q.CreateTodo(ctx, db.CreateTodoParams{
		ID:   id,
		Task: req.Msg.Todo.Task,
	})
	if err != nil {
		return nil, err
	}
	return &connect.Response[todov1.CreateTodoResponse]{
		Msg: &todov1.CreateTodoResponse{
			Todo: &todov1.Todo{
				Id:        StrPtr(todo.ID.String()),
				Task:      todo.Task,
				CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
				UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
				Done:      BoolPtr(todo.Done.Bool),
			},
		},
	}, nil
}

func (srv *TodoServer) ListTodos(ctx context.Context, req *connect.Request[todov1.ListTodosRequest]) (*connect.Response[todov1.ListTodosResponse], error) {
	todos, err := db.Q.ListTodos(ctx)
	if err != nil {
		return nil, err
	}
	responseTodos := []*todov1.Todo{}
	for _, todo := range todos {
		responseTodos = append(responseTodos, &todov1.Todo{
			Id:        StrPtr(todo.ID.String()),
			Task:      todo.Task,
			CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
			UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
			Done:      BoolPtr(todo.Done.Bool),
		})
	}
	slices.SortFunc(responseTodos, func(a, b *todov1.Todo) int {
		dateCmp := cmp.Compare(a.GetCreatedAt(), b.GetCreatedAt())
		return dateCmp
	})
	slices.SortFunc(responseTodos, func(a, b *todov1.Todo) int {
		var boolToInt = func(b bool) int {
			if b {
				return 1
			} else {
				return 0
			}
		}
		doneCmp := cmp.Compare(boolToInt(*a.Done), boolToInt(*b.Done))
		return doneCmp
	})

	return &connect.Response[todov1.ListTodosResponse]{
		Msg: &todov1.ListTodosResponse{
			Todos: responseTodos,
		},
	}, nil
}

func (srv *TodoServer) UpdateTodo(ctx context.Context, req *connect.Request[todov1.UpdateTodoRequest]) (*connect.Response[todov1.UpdateTodoResponse], error) {
	var id pgtype.UUID
	err := id.Scan(*req.Msg.Todo.Id)
	if err != nil {
		return nil, err
	}
	todo, err := db.Q.UpdateTodo(ctx, db.UpdateTodoParams{
		Task: req.Msg.Todo.Task,
		Done: pgtype.Bool{
			Bool:  *req.Msg.Todo.Done,
			Valid: true,
		},
		ID: id,
	})
	if err != nil {
		return nil, err
	}
	return &connect.Response[todov1.UpdateTodoResponse]{
		Msg: &todov1.UpdateTodoResponse{
			Todo: &todov1.Todo{
				Id:        StrPtr(todo.ID.String()),
				Task:      todo.Task,
				CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
				UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
				Done:      BoolPtr(todo.Done.Bool),
			},
		},
	}, nil

}

func (srv *TodoServer) DeleteTodo(ctx context.Context, req *connect.Request[todov1.DeleteTodoRequest]) (*connect.Response[todov1.DeleteTodoResponse], error) {
	var id pgtype.UUID
	err := id.Scan(*req.Msg.Todo.Id)
	if err != nil {
		return nil, err
	}
	err = db.Q.DeleteTodo(ctx, id)
	if err != nil {
		return nil, err
	}
	return &connect.Response[todov1.DeleteTodoResponse]{
		Msg: &todov1.DeleteTodoResponse{},
	}, nil
}

func GetPathHandler() (path string, handler http.Handler) {
	path, handler = todov1connect.NewTodoServiceHandler(&TodoServer{}, connect.WithInterceptors(validate.NewInterceptor()))
	return
}
