package drive

import (
	"errors"

	"github.com/get-drexa/drexa/internal/account"
	"github.com/get-drexa/drexa/internal/httperr"
	"github.com/get-drexa/drexa/internal/organization"
	"github.com/get-drexa/drexa/internal/reqctx"
	"github.com/get-drexa/drexa/internal/user"
	"github.com/get-drexa/drexa/internal/virtualfs"
	"github.com/gofiber/fiber/v2"
	"github.com/uptrace/bun"
)

type driveInfoResponse struct {
	*Drive
	RootDirID string `json:"rootDirId"`
}

type HTTPHandler struct {
	driveService   *Service
	accountService *account.Service
	vfs            *virtualfs.VirtualFS
	db             *bun.DB
	authMiddleware fiber.Handler
}

func NewHTTPHandler(driveService *Service, accountService *account.Service, vfs *virtualfs.VirtualFS, db *bun.DB, authMiddleware fiber.Handler) *HTTPHandler {
	return &HTTPHandler{
		driveService:   driveService,
		accountService: accountService,
		vfs:            vfs,
		db:             db,
		authMiddleware: authMiddleware,
	}
}

func (h *HTTPHandler) RegisterRoutes(api fiber.Router) *virtualfs.ScopedRouter {
	api.Get("/drives", h.authMiddleware, h.listDrives)

	drive := api.Group("/drives/:driveID")
	drive.Use(h.authMiddleware)
	drive.Use(h.driveMiddleware)

	drive.Get("/", h.getDriveInfo)

	return &virtualfs.ScopedRouter{Router: drive}
}

func (h *HTTPHandler) listDrives(c *fiber.Ctx) error {
	u := reqctx.AuthenticatedUser(c).(*user.User)
	org, ok := reqctx.CurrentOrganization(c).(*organization.Organization)
	if !ok || org == nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	acc, err := h.accountService.FindUserAccountInOrg(c.Context(), h.db, org.ID, u.ID)
	if err != nil {
		if errors.Is(err, account.ErrAccountNotFound) {
			return c.SendStatus(fiber.StatusNotFound)
		}
		return httperr.Internal(err)
	}
	if acc.Status != account.StatusActive {
		return c.SendStatus(fiber.StatusNotFound)
	}

	drives, err := h.driveService.ListAccessibleDrives(c.Context(), h.db, org.ID, acc.ID)
	if err != nil {
		return httperr.Internal(err)
	}
	return c.JSON(drives)
}

func (h *HTTPHandler) getDriveInfo(c *fiber.Ctx) error {
	drive, ok := reqctx.CurrentDrive(c).(*Drive)
	if !ok || drive == nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	rootDirID, _ := c.Locals("rootDirId").(string)
	if rootDirID == "" {
		scopeAny := reqctx.VFSAccessScope(c)
		scope, ok := scopeAny.(*virtualfs.Scope)
		if !ok || scope == nil {
			return c.SendStatus(fiber.StatusUnauthorized)
		}

		root, err := h.vfs.FindNode(c.Context(), h.db, scope.RootNodeID.String(), scope)
		if err != nil {
			if errors.Is(err, virtualfs.ErrNodeNotFound) || errors.Is(err, virtualfs.ErrAccessDenied) {
				return c.SendStatus(fiber.StatusNotFound)
			}
			return httperr.Internal(err)
		}
		rootDirID = root.PublicID
	}

	return c.JSON(driveInfoResponse{
		Drive:     drive,
		RootDirID: rootDirID,
	})
}

func (h *HTTPHandler) driveMiddleware(c *fiber.Ctx) error {
	org, ok := reqctx.CurrentOrganization(c).(*organization.Organization)
	if !ok || org == nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	driveID := c.Params("driveID")
	drive, err := h.driveService.DriveByPublicID(c.Context(), h.db, driveID)
	if err != nil {
		if errors.Is(err, ErrDriveNotFound) {
			return c.SendStatus(fiber.StatusNotFound)
		}
		return httperr.Internal(err)
	}
	if drive.OrgID != org.ID {
		return c.SendStatus(fiber.StatusNotFound)
	}

	acc, ok := reqctx.CurrentAccount(c).(*account.Account)
	if !ok || acc == nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	if !h.driveService.CanAccessDrive(drive, acc.OrgID, acc.ID) {
		return c.SendStatus(fiber.StatusNotFound)
	}

	root, err := h.vfs.FindRootDirectory(c.Context(), h.db, drive.ID)
	if err != nil {
		return httperr.Internal(err)
	}

	c.Locals("rootDirId", root.PublicID)

	scope := &virtualfs.Scope{
		DriveID:      drive.ID,
		RootNodeID:   root.ID,
		AllowedOps:   virtualfs.AllAllowedOps,
		AllowedNodes: nil,
		ActorKind:    virtualfs.ScopeActorAccount,
		ActorID:      acc.ID,
	}

	reqctx.SetCurrentDrive(c, drive)
	reqctx.SetVFSAccessScope(c, scope)

	return c.Next()
}
