package handlers

import (
	"errors"
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2"

	filemanager "wucher/internal/domain/file_manager"
	"wucher/internal/shared/pkg/apperrorsx"
	"wucher/internal/transport/http/jsonapi"
)

func writeMissionFileError(c *fiber.Ctx, def apperrorsx.Def, detail string, source *jsonapi.ErrorSource) error {
	msg := strings.TrimSpace(detail)
	if msg == "" {
		msg = def.Message
	}
	c.Locals(apperrorsx.ContextKeyHTTPErrorDetail, msg)
	return jsonapi.WriteErrors(c, def.Status, []jsonapi.ErrorObject{{
		Status:    strconv.Itoa(def.Status),
		Code:      def.Code,
		ErrorCode: def.ErrorCode,
		Title:     def.Title,
		Detail:    msg,
		Source:    source,
	}})
}

func writeFileManagerActionError(c *fiber.Ctx, action string, err error) error {
	def := mapFileManagerActionDef(action, err)
	logHandlerError(c, action, def.Status, def.Code, def.ErrorCode, err)

	appErr := apperrorsx.New(def)
	if err != nil {
		appErr.Cause = err
	}
	return apperrorsx.Write(c, appErr)
}

func writeFileManagerError(c *fiber.Ctx, statusCode int, title, detail string, source *jsonapi.ErrorSource) error {
	def := inferFileManagerDef(statusCode, title, detail)
	appErr := apperrorsx.New(def)
	appErr.Status = statusCode
	appErr.Source = source
	return apperrorsx.Write(c, appErr)
}

func inferFileManagerCode(statusCode int, title, detail string) (string, string) {
	def := inferFileManagerDef(statusCode, title, detail)
	return def.Code, def.ErrorCode
}

func inferFileManagerDef(statusCode int, title, detail string) apperrorsx.Def {
	lowerTitle := strings.ToLower(strings.TrimSpace(title))
	lowerDetail := strings.ToLower(strings.TrimSpace(detail))
	switch statusCode {
	case fiber.StatusServiceUnavailable:
		switch {
		case strings.Contains(lowerTitle, "storage"):
			return apperrorsx.ErrFileManagerStorageUnavailable
		case strings.Contains(lowerTitle, "queue"):
			return apperrorsx.ErrFileManagerQueueUnavailable
		case strings.Contains(lowerTitle, "upload intent"):
			return apperrorsx.ErrFileManagerUploadIntentUnavailable
		case strings.Contains(lowerTitle, "attachment"):
			return apperrorsx.ErrFileManagerAttachmentServiceUnavailable
		default:
			return apperrorsx.ErrFileManagerActionFailed
		}
	case fiber.StatusNotFound:
		switch {
		case strings.Contains(lowerDetail, "folder"):
			return apperrorsx.ErrFileManagerFolderNotFound
		case strings.Contains(lowerDetail, "attachment"):
			return apperrorsx.ErrFileManagerAttachmentNotFound
		case strings.Contains(lowerDetail, "upload intent"):
			return apperrorsx.ErrFileManagerUploadIntentNotFound
		default:
			return apperrorsx.ErrFileManagerFileNotFound
		}
	case fiber.StatusConflict:
		if strings.Contains(lowerTitle, "upload intent") || strings.Contains(lowerDetail, "upload intent") {
			return apperrorsx.ErrFileManagerUploadIntentConflict
		}
		return apperrorsx.ErrFileManagerStatusConflict
	case fiber.StatusUnprocessableEntity:
		return apperrorsx.ErrFileManagerValidationFailed
	case fiber.StatusUnauthorized:
		return apperrorsx.ErrUserUnauthorized
	case fiber.StatusBadRequest:
		if strings.Contains(lowerTitle, "invalid json") {
			return apperrorsx.ErrInvalidRequest
		}
		return apperrorsx.ErrFileManagerActionFailed
	default:
		return apperrorsx.ErrFileManagerActionFailed
	}
}

func mapFileManagerActionDef(action string, err error) apperrorsx.Def {
	statusCode := fiber.StatusBadRequest
	title := action
	def := apperrorsx.ErrFileManagerActionFailed

	switch {
	case errors.Is(err, filemanager.ErrStorageUnavailable):
		statusCode = fiber.StatusServiceUnavailable
		title = "Storage unavailable"
		def = apperrorsx.ErrFileManagerStorageUnavailable
	case errors.Is(err, filemanager.ErrFileProcessingQueueDisabled):
		statusCode = fiber.StatusServiceUnavailable
		title = "Queue unavailable"
		def = apperrorsx.ErrFileManagerQueueUnavailable
	case errors.Is(err, filemanager.ErrUploadIntentRepositoryDisabled):
		statusCode = fiber.StatusServiceUnavailable
		title = "Upload intent unavailable"
		def = apperrorsx.ErrFileManagerUploadIntentUnavailable
	case errors.Is(err, filemanager.ErrFolderNotFound), errors.Is(err, filemanager.ErrFileNotFound):
		statusCode = fiber.StatusNotFound
		title = "Not found"
		if errors.Is(err, filemanager.ErrFolderNotFound) {
			def = apperrorsx.ErrFileManagerFolderNotFound
		} else {
			def = apperrorsx.ErrFileManagerFileNotFound
		}
	case errors.Is(err, filemanager.ErrAttachmentNotFound):
		statusCode = fiber.StatusNotFound
		title = "Attachment not found"
		def = apperrorsx.ErrFileManagerAttachmentNotFound
	case errors.Is(err, filemanager.ErrUploadIntentNotFound):
		statusCode = fiber.StatusNotFound
		title = "Upload intent not found"
		def = apperrorsx.ErrFileManagerUploadIntentNotFound
	case errors.Is(err, filemanager.ErrFileStatusConflict):
		statusCode = fiber.StatusConflict
		title = "Conflict"
		def = apperrorsx.ErrFileManagerStatusConflict
	case errors.Is(err, filemanager.ErrUploadIntentExpired),
		errors.Is(err, filemanager.ErrUploadIntentAlreadyComplete),
		errors.Is(err, filemanager.ErrUploadIntentStatusConflict):
		statusCode = fiber.StatusConflict
		title = "Upload intent conflict"
		def = apperrorsx.ErrFileManagerUploadIntentConflict
	case errors.Is(err, filemanager.ErrDuplicateName),
		errors.Is(err, filemanager.ErrInvalidMove),
		errors.Is(err, filemanager.ErrFolderNotEmpty),
		errors.Is(err, filemanager.ErrFolderNotInTrash),
		errors.Is(err, filemanager.ErrFileNotInTrash),
		errors.Is(err, filemanager.ErrAttachmentAlreadyExists),
		errors.Is(err, filemanager.ErrAttachmentRefRequired),
		errors.Is(err, filemanager.ErrFileNotReadyForAttachment),
		errors.Is(err, filemanager.ErrFileAlreadyFinalized),
		errors.Is(err, filemanager.ErrFileNotEligibleForFinalize),
		errors.Is(err, filemanager.ErrInvalidFileStatusTransition),
		errors.Is(err, filemanager.ErrFileTooLarge),
		errors.Is(err, filemanager.ErrFileMimeTypeNotAllowed),
		errors.Is(err, filemanager.ErrFileExtensionNotAllowed),
		errors.Is(err, filemanager.ErrEmptyFileNotAllowed),
		errors.Is(err, filemanager.ErrFileNameTooLong),
		errors.Is(err, filemanager.ErrUploadObjectNotFound),
		errors.Is(err, filemanager.ErrUploadObjectMismatch),
		errors.Is(err, filemanager.ErrInvalidName),
		errors.Is(err, filemanager.ErrReservedName):
		statusCode = fiber.StatusUnprocessableEntity
		title = "Validation error"
		def = apperrorsx.ErrFileManagerValidationFailed
	case errors.Is(err, filemanager.ErrAttachmentRepositoryDisabled):
		statusCode = fiber.StatusServiceUnavailable
		title = "Attachment service unavailable"
		def = apperrorsx.ErrFileManagerAttachmentServiceUnavailable
	}

	def.Status = statusCode
	def.Title = title
	return def
}

func mapFileManagerActionError(action string, err error) (int, string, string, string, string) {
	def := mapFileManagerActionDef(action, err)
	return def.Status, def.Title, def.Message, def.Code, def.ErrorCode
}
