package lessons

import (
	"errors"
	"fmt"
	"time"

	"git.ex.umbach.dev/LMS/libcore/models"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"lms.de/backend/modules/database"
	"lms.de/backend/modules/structs"
	"lms.de/backend/modules/utils"
	"lms.de/backend/socketclients"
)

func GetQuestions(c *fiber.Ctx) error {
	// swagger:operation GET /v1/lessons/{lessonId}/questions questions getQuestions
	// ---
	// summary: Get questions
	// produces:
	// - application/json
	// responses:
	//   '200':
	//     description: Questions
	//     schema:
	//       "$ref": "#/definitions/QuestionsResponse"
	//   '500':
	//     description: Failed to get questions

	var params structs.LessonIdParam

	if err := c.ParamsParser(&params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// check if lesson belongs to organization

	var lesson models.Lesson

	if err := database.DB.Model(&models.Lesson{}).
		Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
		First(&lesson).Error; err != nil {
		return c.JSON([]models.Question{})
	}

	// get questions

	var questions []models.Question

	if err := database.DB.Model(&models.Question{}).
		Where("lesson_id = ?", params.LessonId).
		Find(&questions).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return c.SendStatus(fiber.StatusNotFound)
		}
	}

	// get all questions liked by user

	var likedQuestions []string

	if err := database.DB.Model(&models.QuestionLike{}).
		Where("creator_user_id = ?", c.Locals("userId").(string)).
		Pluck("question_id", &likedQuestions).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return c.SendStatus(fiber.StatusInternalServerError)
		}
	}

	/*
		var questionLikes []models.QuestionLike

		if err := database.DB.Model(&models.QuestionLike{}).
			Where("creator_user_id = ?", c.Locals("userId").(string)).
			Find(&questionLikes).Error; err != nil {
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return c.SendStatus(fiber.StatusInternalServerError)
			}
		}

		var likedQuestions []string

		for _, questionLike := range questionLikes {
			likedQuestions = append(likedQuestions, questionLike.QuestionId)
		} */

	return c.JSON(structs.QuestionsResponse{
		Questions:      questions,
		LikedQuestions: likedQuestions,
	})
}

func CreateQuestion(c *fiber.Ctx) error {
	// swagger:operation POST /v1/lessons/{lessonId}/questions question createQuestion
	// ---
	// summary: Create question
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/CreateQuestionRequest"
	// responses:
	//   '200':
	//     description: Question created successfully
	//     schema:
	//       type: string
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to create question

	var params structs.LessonIdParam

	if err := c.ParamsParser(&params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	var body structs.CreateQuestionRequest

	if err := c.BodyParser(&body); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// check if lesson belongs to organization

	var lesson models.Lesson

	if err := database.DB.Model(&models.Lesson{}).
		Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
		First(&lesson).Error; err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	// create question

	question := models.Question{
		Id:            uuid.New().String(),
		LessonId:      params.LessonId,
		Message:       body.Message,
		Likes:         0,
		CreatorUserId: c.Locals("userId").(string),
		CreatedAt:     time.Now(),
	}

	if err := database.DB.Create(&question).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	socketclients.BroadcastMessageToTopic(
		c.Locals("organizationId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionCreated,
			Body: question,
		},
	)

	return c.JSON(fiber.Map{
		"status": "success",
	})
}

func CreateQuestionReply(c *fiber.Ctx) error {
	// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/reply question createQuestionReply
	// ---
	// summary: Create question reply
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/CreateQuestionRequest"
	// responses:
	//   '200':
	//     description: Question reply created successfully
	//     schema:
	//       type: string
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to create question reply

	var params structs.LessonIdQuestionIdParam

	if err := c.ParamsParser(&params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	var body structs.CreateQuestionRequest

	if err := c.BodyParser(&body); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// check if lesson belongs to organization

	var lesson models.Lesson

	if err := database.DB.Model(&models.Lesson{}).
		Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
		First(&lesson).Error; err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	// check if question belongs to lesson

	var question models.Question

	if err := database.DB.Model(&models.Question{}).
		Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
		First(&question).Error; err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	// create question reply

	question = models.Question{
		Id:            uuid.New().String(),
		LessonId:      params.LessonId,
		QuestionId:    params.QuestionId,
		ReplyId:       params.QuestionId,
		Message:       body.Message,
		Likes:         0,
		CreatorUserId: c.Locals("userId").(string),
	}

	if err := database.DB.Create(&question).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	socketclients.BroadcastMessageToTopic(
		c.Locals("organizationId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionCreated,
			Body: question,
		},
	)

	return c.JSON(fiber.Map{
		"status": "success",
	})
}

func LikeQuestion(c *fiber.Ctx) error {
	// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/likes question likeQuestion
	// ---
	// summary: Like question
	// produces:
	// - application/json
	// responses:
	//   '200':
	//     description: Question liked successfully
	//     schema:
	//       type: string
	//   '500':
	//     description: Failed to like question

	var params structs.LessonIdQuestionIdParam

	if err := c.ParamsParser(&params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// check if lesson belongs to organization

	var lesson models.Lesson

	if err := database.DB.Model(&models.Lesson{}).
		Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
		First(&lesson).Error; err != nil {
		fmt.Println("lesson not found")
		return c.SendStatus(fiber.StatusNotFound)
	}

	// check if question belongs to lesson

	var question models.Question

	if err := database.DB.Model(&models.Question{}).
		Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
		First(&question).Error; err != nil {
		fmt.Println("question not found")
		return c.SendStatus(fiber.StatusNotFound)
	}

	// check if user already liked question

	var questionLike models.QuestionLike

	if err := database.DB.Model(&models.QuestionLike{}).
		Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
		First(&questionLike).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return c.SendStatus(fiber.StatusInternalServerError)
		}
	}

	if questionLike.Id != "" {
		fmt.Println("question already liked")
		return c.SendStatus(fiber.StatusConflict)
	}

	// like question

	questionLike = models.QuestionLike{
		Id:            uuid.New().String(),
		QuestionId:    params.QuestionId,
		CreatorUserId: c.Locals("userId").(string),
		CreatedAt:     time.Now(),
	}

	if err := database.DB.Create(&questionLike).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	// update question likes

	if err := database.DB.Model(&models.Question{}).
		Where("id = ?", params.QuestionId).
		Update("likes", gorm.Expr("likes + 1")).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	// send message to user clients who liked the question
	socketclients.SendMessageToUserWithTopic(
		c.Locals("userId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionLiked,
			Body: question.Id,
		},
	)

	// inform all users about the like
	socketclients.BroadcastMessageToTopic(
		c.Locals("userId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionCountUpLikes,
			Body: question.Id,
		},
	)

	return c.JSON(fiber.Map{
		"status": "success",
	})
}

func DislikeQuestion(c *fiber.Ctx) error {
	// swagger:operation DELETE /v1/lessons/{lessonId}/questions/{questionId}/likes question dislikeQuestion
	// ---
	// summary: Dislike question
	// produces:
	// - application/json
	// responses:
	//   '200':
	//     description: Question disliked successfully
	//     schema:
	//       type: string
	//   '500':
	//     description: Failed to dislike question

	var params structs.LessonIdQuestionIdParam

	if err := c.ParamsParser(&params); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// check if lesson belongs to organization

	var lesson models.Lesson

	if err := database.DB.Model(&models.Lesson{}).
		Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
		First(&lesson).Error; err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	// check if question belongs to lesson

	var question models.Question

	if err := database.DB.Model(&models.Question{}).
		Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
		First(&question).Error; err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	// check if user already liked question

	var questionLike models.QuestionLike

	if err := database.DB.Model(&models.QuestionLike{}).
		Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
		First(&questionLike).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return c.SendStatus(fiber.StatusInternalServerError)
		}
	}

	if questionLike.Id == "" {
		return c.SendStatus(fiber.StatusConflict)
	}

	// dislike question

	if err := database.DB.Delete(&questionLike).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	// update question likes

	if err := database.DB.Model(&models.Question{}).
		Where("id = ?", params.QuestionId).
		Update("likes", gorm.Expr("likes - 1")).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	// send message to user clients who disliked the question
	socketclients.SendMessageToUserWithTopic(
		c.Locals("userId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionDisliked,
			Body: question.Id,
		},
	)

	// inform all users about the dislike
	socketclients.BroadcastMessageToTopic(
		c.Locals("userId").(string),
		utils.SubscribedTopicLessonsId(params.LessonId),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdLessonQuestionCountDownLikes,
			Body: question.Id,
		},
	)

	return c.JSON(fiber.Map{
		"status": "success",
	})
}
