package result

import (
	"bytes"
	"gitea.pena/SQuiz/common/dal"
	"gitea.pena/SQuiz/common/middleware"
	"gitea.pena/SQuiz/common/model"
	"gitea.pena/SQuiz/common/repository/result"
	"gitea.pena/SQuiz/core/internal/tools"
	"github.com/gofiber/fiber/v2"
	"strconv"
	"time"
)

type Deps struct {
	DAL      *dal.DAL
	S3Prefix string
}

type Result struct {
	dal      *dal.DAL
	s3Prefix string
}

func NewResultController(deps Deps) *Result {
	return &Result{
		dal:      deps.DAL,
		s3Prefix: deps.S3Prefix,
	}
}

type ReqExport struct {
	To, From time.Time
	New      bool
	Page     uint64
	Limit    uint64
}

type ReqExportResponse struct {
	TotalCount uint64               `json:"total_count"`
	Results    []model.AnswerExport `json:"results"`
}

func (r *Result) GetResultsByQuizID(ctx *fiber.Ctx) error {
	payment := true // параметр для определения существования текущих привилегий юзера

	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	var req ReqExport
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	quizIDStr := ctx.Params("quizId")
	quizID, err := strconv.ParseUint(quizIDStr, 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format")
	}

	account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}
	if _, cnt := account.Privileges["quizCnt"]; !cnt {
		if _, unlim := account.Privileges["quizUnlimTime"]; !unlim {
			payment = false
		}
	}

	results, totalCount, err := r.dal.ResultRepo.GetQuizResults(ctx.Context(), quizID, result.GetQuizResDeps{
		To:    req.To,
		From:  req.From,
		New:   req.New,
		Page:  req.Page,
		Limit: req.Limit,
	}, payment)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	resp := &ReqExportResponse{
		TotalCount: totalCount,
		Results:    results,
	}

	return ctx.Status(fiber.StatusOK).JSON(resp)
}

func (r *Result) DelResultByID(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token")
	}

	resultIDStr := ctx.Params("resultId")
	resultID, err := strconv.ParseUint(resultIDStr, 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid result ID format")
	}

	isOwner, err := r.dal.ResultRepo.CheckResultOwner(ctx.Context(), resultID, accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if !isOwner {
		return ctx.Status(fiber.StatusUnauthorized).SendString("not the owner of the result")
	}

	if err := r.dal.ResultRepo.SoftDeleteResultByID(ctx.Context(), resultID); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	ctx.Status(fiber.StatusOK)
	return nil
}

type ReqSeen struct {
	Answers []int64
}

func (r *Result) SetStatus(ctx *fiber.Ctx) error {
	var req ReqSeen
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token")
	}

	answers, err := r.dal.ResultRepo.CheckResultsOwner(ctx.Context(), req.Answers, accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if len(answers) != len(req.Answers) {
		return ctx.Status(fiber.StatusNotAcceptable).SendString("could not update some answers because you don't have rights")
	}

	if err := r.dal.ResultRepo.UpdateAnswersStatus(ctx.Context(), accountID, answers); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	return ctx.Status(fiber.StatusOK).JSON(nil)
}

func (r *Result) ExportResultsToCSV(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	quizIDStr := ctx.Params("quizID")
	quizID, err := strconv.ParseUint(quizIDStr, 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("invalid quiz ID")
	}

	req := ReqExport{}
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("invalid request body")
	}

	account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if _, cnt := account.Privileges["quizCnt"]; !cnt {
		if _, unlim := account.Privileges["quizUnlimTime"]; !unlim {
			return ctx.Status(fiber.StatusPaymentRequired).SendString("payment required")
		}
	}

	quiz, err := r.dal.QuizRepo.GetQuizById(ctx.Context(), accountID, quizID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get quiz")
	}

	questions, err := r.dal.ResultRepo.GetQuestions(ctx.Context(), quizID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get questions")
	}

	answers, err := r.dal.ResultRepo.GetQuizResultsCSV(ctx.Context(), quizID, result.GetQuizResDeps{
		To:    req.To,
		From:  req.From,
		New:   req.New,
		Page:  req.Page,
		Limit: req.Limit,
	})
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get quiz results")
	}

	buffer := new(bytes.Buffer)

	if err := tools.WriteDataToExcel(buffer, questions, answers, r.s3Prefix+quiz.Qid+"/"); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed to write data to Excel")
	}

	ctx.Set(fiber.HeaderContentType, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	ctx.Set(fiber.HeaderContentDisposition, `attachment; filename="results.xlsx"`)

	return ctx.Send(buffer.Bytes())
}

func (r *Result) GetResultAnswers(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	resultID, err := strconv.ParseUint(ctx.Params("resultID"), 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("invalid quiz ID")
	}

	account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if _, cnt := account.Privileges["quizCnt"]; !cnt {
		if _, unlim := account.Privileges["quizUnlimTime"]; !unlim {
			return ctx.Status(fiber.StatusPaymentRequired).SendString("payment required")
		}
	}

	answers, err := r.dal.ResultRepo.GetResultAnswers(ctx.Context(), resultID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get result answers")
	}
	sortedAnswers, err := r.dal.QuestionRepo.ForSortingResults(ctx.Context(), answers)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString("failed sort result answers")
	}

	return ctx.JSON(sortedAnswers)
}
