package organization

import (
	"errors"
	"strings"
	"time"

	"git.ex.umbach.dev/Alex/roese-utils/rsutils"
	"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 GetOrganizationSettings(c *fiber.Ctx) error {
	// swagger:operation GET /organization/settings organization getOrganizationSettings
	// ---
	// summary: Get settings
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// responses:
	//   '200':
	//     description: Settings fetched successfully
	//     schema:
	//       "$ref": "#/definitions/GetOrganizationSettingsResponse"
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to fetch settings

	var organizationSettings structs.GetOrganizationSettingsResponse

	if err := database.DB.Model(&models.Organization{}).
		Select("subdomain", "company_name", "primary_color", "logo_url", "banner_url").
		Where("id = ?", c.Locals("organizationId").(string)).
		First(&organizationSettings).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	return c.JSON(organizationSettings)
}

func UpdateOrganizationSettings(c *fiber.Ctx) error {
	// swagger:operation PATCH /organization/settings organization updateOrganizationSettings
	// ---
	// summary: Update settings
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/GetOrganizationSettingsResponse"
	// responses:
	//   '200':
	//     description: Settings updated successfully
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to update settings

	var organizationSettings structs.UpdateOrganizationSettingsRequest

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

	if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(organizationSettings).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	socketclients.BroadcastMessageExceptBrowserTabSession(
		c.Locals("organizationId").(string),
		c.Locals("browserTabSession").(string),
		structs.SendSocketMessage{
			Cmd:  utils.SendCmdSettingsUpdated,
			Body: organizationSettings,
		},
	)

	return c.SendStatus(fiber.StatusOK)
}

func UpdateOrganizationFile(c *fiber.Ctx) error {
	// swagger:operation POST /organization/file/{type} organization updateOrganizationFiles
	// ---
	// summary: Update files
	// consumes:
	// - multipart/form-data
	// produces:
	// - application/json
	// parameters:
	// - name: logo
	//   in: formData
	//   type: file
	//   required: false
	// - name: banner
	//   in: formData
	//   type: file
	//   required: false
	// responses:
	//   '200':
	//     description: Files updated successfully
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to update files

	var params structs.UpdateOrganizationFileParam

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

	fileHeader, err := c.FormFile("file")

	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if params.Type == "logo" || params.Type == "banner" {
		if fileHeader.Size > utils.MaxImageSize {
			return c.SendStatus(fiber.StatusBadRequest)
		}

		if !utils.IsFileTypeAllowed(fileHeader.Header.Get("Content-Type"), utils.AcceptedImageFileTypes) {
			return c.SendStatus(fiber.StatusBadRequest)
		}
	} else {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	// get current file

	organization := models.Organization{}

	selectField := "logo_url"

	if params.Type == "banner" {
		selectField = "banner_url"
	}

	if err := database.DB.Model(&models.Organization{
		Id: c.Locals("organizationId").(string),
	}).Select(selectField).First(&organization).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	// delete current file

	if organization.LogoUrl != "" {
		utils.DeleteFile(organization.LogoUrl)
	}

	if organization.BannerUrl != "" {
		utils.DeleteFile(organization.BannerUrl)
	}

	fileName := uuid.New().String() + "." + strings.Split(fileHeader.Header["Content-Type"][0], "/")[1]

	databasePath, publicPath := utils.GetFullImagePath(c.Locals("organizationId").(string), "")

	utils.CreateFolderStructureIfNotExists(publicPath)

	update := models.Organization{}

	if params.Type == "logo" {
		update.LogoUrl = databasePath + fileName
	} else {
		update.BannerUrl = databasePath + fileName
	}

	if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(update).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to save file",
		})
	}

	cmdId := utils.SendCmdSettingsUpdatedLogo

	if params.Type == "banner" {
		cmdId = utils.SendCmdSettingsUpdatedBanner
	}

	newPath := databasePath + fileName

	socketclients.BroadcastMessageExceptBrowserTabSession(
		c.Locals("organizationId").(string),
		c.Locals("browserTabSession").(string),
		structs.SendSocketMessage{
			Cmd:  cmdId,
			Body: newPath,
		},
	)

	return c.JSON(fiber.Map{
		"Data": newPath,
	})
}

func UpdateSubdomain(c *fiber.Ctx) error {
	// swagger:operation PATCH /organization/subdomain/{subdomain} organization updateSubdomain
	// ---
	// summary: Update organization subdomain
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: subdomain
	//   in: path
	//   required: true
	//   type: string
	// responses:
	//   '200':
	//     description: Subdomain updated successfully
	//   '400':
	//     description: Invalid request body
	//   '500':
	//     description: Failed to update subdomain

	var params structs.SubdomainParam

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

	var organization models.Organization

	if err := database.DB.Select("subdomain").Model(organization).Where("id = ?", c.Locals("organizationId").(string)).First(&organization).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return c.SendStatus(fiber.StatusNotFound)
		}
	}
	if organization.Subdomain == "" {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if err := database.DB.Model(&organization).Where("id = ?", c.Locals("organizationId")).Update("subdomain", params.Subdomain).Error; err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	orgId := c.Locals("organizationId").(string)

	// send broadcast with delay
	go func() {
		time.Sleep(1 * time.Second)

		socketclients.BroadcastMessage(orgId,
			structs.SendSocketMessage{
				Cmd:  utils.SendCmdSettingsUpdatedSubdomain,
				Body: params.Subdomain,
			},
		)
	}()

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