package rest

import (
	"git.sch9.ru/new_gate/ms-tester/internal/models"
	"git.sch9.ru/new_gate/ms-tester/internal/tester"
	"git.sch9.ru/new_gate/ms-tester/pkg"
	testerv1 "git.sch9.ru/new_gate/ms-tester/proto/tester/v1"
	"github.com/gofiber/fiber/v2"
)

type TesterHandlers struct {
	problemsUC tester.ProblemUseCase
	contestsUC tester.ContestUseCase
}

func NewTesterHandlers(problemsUC tester.ProblemUseCase, contestsUC tester.ContestRepository) *TesterHandlers {
	return &TesterHandlers{
		problemsUC: problemsUC,
		contestsUC: contestsUC,
	}
}

func (h *TesterHandlers) ListContests(c *fiber.Ctx, params testerv1.ListContestsParams) error {
	contests, count, err := h.contestsUC.ListContests(c.Context(), params.Page, params.PageSize)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	resp := testerv1.ListContestsResponse{
		Contests: make([]testerv1.ContestsListItem, len(contests)),
		Page:     params.Page,
		MaxPage: func() int32 {
			if count%params.PageSize == 0 {
				return count / params.PageSize
			}
			return count/params.PageSize + 1
		}(),
	}

	for i, contest := range contests {
		resp.Contests[i] = testerv1.ContestsListItem{
			Id:        contest.Id,
			Title:     contest.Title,
			CreatedAt: contest.CreatedAt,
			UpdatedAt: contest.UpdatedAt,
		}
	}

	return c.JSON(resp)
}

func (h *TesterHandlers) ListProblems(c *fiber.Ctx, params testerv1.ListProblemsParams) error {
	problems, count, err := h.problemsUC.ListProblems(c.Context(), params.Page, params.PageSize)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	resp := testerv1.ListProblemsResponse{
		Problems: make([]testerv1.ProblemListItem, len(problems)),
		Page:     params.Page,
		MaxPage: func() int32 {
			if count%params.PageSize == 0 {
				return count / params.PageSize
			}
			return count/params.PageSize + 1
		}(),
	}

	for i, problem := range problems {
		resp.Problems[i] = testerv1.ProblemListItem{
			Id:          problem.Id,
			Title:       problem.Title,
			MemoryLimit: problem.MemoryLimit,
			TimeLimit:   problem.TimeLimit,
			CreatedAt:   problem.CreatedAt,
			UpdatedAt:   problem.UpdatedAt,
		}
	}

	return c.JSON(resp)
}

func (h *TesterHandlers) CreateContest(c *fiber.Ctx) error {
	id, err := h.contestsUC.CreateContest(c.Context(), "Название контеста")
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.JSON(testerv1.CreateContestResponse{
		Id: id,
	})
}

func (h *TesterHandlers) DeleteContest(c *fiber.Ctx, id int32) error {
	err := h.contestsUC.DeleteContest(c.Context(), id)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}

func (h *TesterHandlers) GetContest(c *fiber.Ctx, id int32) error {
	contest, err := h.contestsUC.ReadContestById(c.Context(), id)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	//token, ok := c.Locals(TokenKey).(*models.JWT)
	//if !ok {
	//	return c.SendStatus(fiber.StatusUnauthorized)
	//}

	tasks, err := h.contestsUC.ReadRichTasks(c.Context(), id)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	resp := testerv1.GetContestResponse{
		Contest: testerv1.Contest{
			Id:        id,
			Title:     *contest.Title,
			CreatedAt: *contest.CreatedAt,
			UpdatedAt: *contest.UpdatedAt,
		},
		Tasks: make([]struct {
			BestSolution testerv1.BestSolution `json:"best_solution"`
			Task         testerv1.RichTask     `json:"task"`
		}, len(tasks)),
	}

	for i, task := range tasks {
		resp.Tasks[i] = struct {
			BestSolution testerv1.BestSolution `json:"best_solution"`
			Task         testerv1.RichTask     `json:"task"`
		}{
			BestSolution: testerv1.BestSolution{},
			Task: testerv1.RichTask{
				Id:          task.Id,
				ProblemId:   task.ProblemId,
				Position:    task.Position,
				Title:       task.Title,
				MemoryLimit: task.MemoryLimit,
				TimeLimit:   task.TimeLimit,
				CreatedAt:   task.CreatedAt,
				UpdatedAt:   task.UpdatedAt,
			},
		}
	}

	return c.JSON(resp)
}

func (h *TesterHandlers) DeleteParticipant(c *fiber.Ctx, params testerv1.DeleteParticipantParams) error {
	err := h.contestsUC.DeleteParticipant(c.Context(), params.ParticipantId)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}

func (h *TesterHandlers) AddParticipant(c *fiber.Ctx, params testerv1.AddParticipantParams) error {
	id, err := h.contestsUC.AddParticipant(c.Context(), params.ContestId, params.UserId)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.JSON(testerv1.AddParticipantResponse{
		Id: id,
	})
}

func (h *TesterHandlers) DeleteTask(c *fiber.Ctx, params testerv1.DeleteTaskParams) error {
	err := h.contestsUC.DeleteTask(c.Context(), params.TaskId)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}

func (h *TesterHandlers) AddTask(c *fiber.Ctx, params testerv1.AddTaskParams) error {
	id, err := h.contestsUC.AddTask(c.Context(), params.ContestId, params.ProblemId)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.JSON(testerv1.AddTaskResponse{
		Id: id,
	})
}

func (h *TesterHandlers) CreateProblem(c *fiber.Ctx) error {
	id, err := h.problemsUC.CreateProblem(c.Context(), "Название задачи")
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.JSON(testerv1.CreateProblemResponse{
		Id: id,
	})
}

func (h *TesterHandlers) DeleteProblem(c *fiber.Ctx, id int32) error {
	err := h.problemsUC.DeleteProblem(c.Context(), id)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)

}

func (h *TesterHandlers) GetProblem(c *fiber.Ctx, id int32) error {
	problem, err := h.problemsUC.ReadProblemById(c.Context(), id)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.JSON(
		testerv1.GetProblemResponse{Problem: testerv1.Problem{
			Id:           problem.Id,
			Title:        problem.Title,
			Legend:       problem.Legend,
			InputFormat:  problem.InputFormat,
			OutputFormat: problem.OutputFormat,
			Notes:        problem.Notes,
			Tutorial:     problem.Tutorial,
			LatexSummary: problem.LatexSummary,
			TimeLimit:    problem.TimeLimit,
			MemoryLimit:  problem.MemoryLimit,
			CreatedAt:    problem.CreatedAt,
			UpdatedAt:    problem.UpdatedAt,
		}},
	)
}

func (h *TesterHandlers) ListParticipants(c *fiber.Ctx, params testerv1.ListParticipantsParams) error {
	participants, count, err := h.contestsUC.ListParticipants(c.Context(), params.ContestId, params.Page, params.PageSize)
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	resp := testerv1.ListParticipantsResponse{
		Participants: make([]testerv1.ParticipantsListItem, len(participants)),
		Page:         params.Page,
		MaxPage: func() int32 {
			if count%params.PageSize == 0 {
				return count / params.PageSize
			}
			return count/params.PageSize + 1
		}(),
	}

	for i, participant := range participants {
		resp.Participants[i] = testerv1.ParticipantsListItem{
			Id:        participant.Id,
			UserId:    participant.UserId,
			Name:      participant.Name,
			CreatedAt: participant.CreatedAt,
			UpdatedAt: participant.UpdatedAt,
		}
	}

	return c.JSON(resp)
}

func (h *TesterHandlers) UpdateProblem(c *fiber.Ctx, id int32) error {
	var req testerv1.UpdateProblemRequest
	err := c.BodyParser(&req)
	if err != nil {
		return err
	}

	err = h.problemsUC.UpdateProblem(c.Context(), id, models.ProblemUpdate{
		Title:        req.Title,
		Legend:       req.Legend,
		InputFormat:  req.InputFormat,
		OutputFormat: req.OutputFormat,
		Notes:        req.Notes,
		Tutorial:     req.Tutorial,
		MemoryLimit:  req.MemoryLimit,
		TimeLimit:    req.TimeLimit,
	})

	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}

func (h *TesterHandlers) UpdateContest(c *fiber.Ctx, id int32) error {
	var req testerv1.UpdateContestRequest
	err := c.BodyParser(&req)
	if err != nil {
		return err
	}

	err = h.contestsUC.UpdateContest(c.Context(), id, models.ContestUpdate{
		Title: req.Title,
	})
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}

func (h *TesterHandlers) UpdateParticipant(c *fiber.Ctx, params testerv1.UpdateParticipantParams) error {
	var req testerv1.UpdateParticipantRequest
	err := c.BodyParser(&req)
	if err != nil {
		return err
	}

	err = h.contestsUC.UpdateParticipant(c.Context(), params.ParticipantId, models.ParticipantUpdate{
		Name: req.Name,
	})
	if err != nil {
		return c.SendStatus(pkg.ToREST(err))
	}

	return c.SendStatus(fiber.StatusOK)
}