package drive

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/ultisuite/ulti-backend/internal/drivestore"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
)

type OrgFolderView struct {
	ID              string  `json:"id"`
	OrgSlug         string  `json:"org_slug"`
	MountPoint      string  `json:"mount_point"`
	NCFolderID      int     `json:"nc_folder_id"`
	QuotaBytes      *int64  `json:"quota_bytes,omitempty"`
	AutoProvisioned bool    `json:"auto_provisioned"`
	Permissions     int     `json:"permissions,omitempty"`
	Size            int64   `json:"size,omitempty"`
}

func (s *Service) ListOrgFoldersForUser(ctx context.Context, ncUserID string) ([]OrgFolderView, error) {
	store := s.ensureStore()
	if store == nil {
		return nil, fmt.Errorf("store not configured")
	}
	rows, err := store.ListOrgFolders(ctx)
	if err != nil {
		return nil, err
	}
	ncFolders, err := s.nc.ListGroupFolders(ctx, ncUserID)
	if err != nil {
		return nil, mapDriveError(err)
	}
	accessible := make(map[int]nextcloud.GroupFolder, len(ncFolders))
	for _, f := range ncFolders {
		accessible[f.ID] = f
	}
	out := make([]OrgFolderView, 0, len(rows))
	for _, row := range rows {
		ncFolder, ok := accessible[row.NCFolderID]
		if !ok {
			continue
		}
		perms := 0
		for _, p := range ncFolder.Groups {
			if p > perms {
				perms = p
			}
		}
		out = append(out, OrgFolderView{
			ID:              row.ID,
			OrgSlug:         row.OrgSlug,
			MountPoint:      row.MountPoint,
			NCFolderID:      row.NCFolderID,
			QuotaBytes:      row.QuotaBytes,
			AutoProvisioned: row.AutoProvisioned,
			Permissions:     perms,
			Size:            ncFolder.Size,
		})
	}
	return out, nil
}

func (s *Service) ListOrgFoldersAdmin(ctx context.Context) ([]OrgFolderView, error) {
	store := s.ensureStore()
	if store == nil {
		return nil, fmt.Errorf("store not configured")
	}
	rows, err := store.ListOrgFolders(ctx)
	if err != nil {
		return nil, err
	}
	out := make([]OrgFolderView, 0, len(rows))
	for _, row := range rows {
		out = append(out, OrgFolderView{
			ID:              row.ID,
			OrgSlug:         row.OrgSlug,
			MountPoint:      row.MountPoint,
			NCFolderID:      row.NCFolderID,
			QuotaBytes:      row.QuotaBytes,
			AutoProvisioned: row.AutoProvisioned,
		})
	}
	return out, nil
}

type CreateOrgFolderParams struct {
	OrgSlug         string
	MountPoint      string
	QuotaBytes      *int64
	AutoProvisioned bool
	CreatedBy       string
}

func (s *Service) CreateOrgFolder(ctx context.Context, p CreateOrgFolderParams) (OrgFolderView, error) {
	store := s.ensureStore()
	if store == nil {
		return OrgFolderView{}, fmt.Errorf("store not configured")
	}
	orgSlug := strings.TrimSpace(strings.ToLower(p.OrgSlug))
	mountPoint := strings.TrimSpace(p.MountPoint)
	if orgSlug == "" || mountPoint == "" {
		return OrgFolderView{}, ErrInvalid
	}
	if _, err := store.GetOrgFolderBySlug(ctx, orgSlug); err == nil {
		return OrgFolderView{}, ErrConflict
	} else if !errors.Is(err, drivestore.ErrOrgFolderNotFound) {
		return OrgFolderView{}, err
	}

	groupID := nextcloud.OrgGroupID(orgSlug)
	if err := s.nc.EnsureGroup(ctx, groupID); err != nil {
		return OrgFolderView{}, mapDriveError(err)
	}
	ncFolderID, err := s.nc.CreateGroupFolder(ctx, mountPoint)
	if err != nil {
		return OrgFolderView{}, mapDriveError(err)
	}
	if err := s.nc.AssignGroupToFolder(ctx, ncFolderID, groupID, 31); err != nil {
		_ = s.nc.DeleteGroupFolder(ctx, ncFolderID)
		return OrgFolderView{}, mapDriveError(err)
	}
	if p.QuotaBytes != nil {
		if err := s.nc.SetGroupFolderQuota(ctx, ncFolderID, *p.QuotaBytes); err != nil {
			return OrgFolderView{}, mapDriveError(err)
		}
	}
	row, err := store.CreateOrgFolder(ctx, drivestore.CreateOrgFolderParams{
		OrgSlug:         orgSlug,
		NCFolderID:      ncFolderID,
		MountPoint:      mountPoint,
		QuotaBytes:      p.QuotaBytes,
		AutoProvisioned: p.AutoProvisioned,
		CreatedBy:       p.CreatedBy,
	})
	if err != nil {
		_ = s.nc.DeleteGroupFolder(ctx, ncFolderID)
		return OrgFolderView{}, err
	}
	return OrgFolderView{
		ID:         row.ID,
		OrgSlug:    row.OrgSlug,
		MountPoint: row.MountPoint,
		NCFolderID: row.NCFolderID,
		QuotaBytes: row.QuotaBytes,
	}, nil
}

func (s *Service) ProvisionOrgFolder(ctx context.Context, orgSlug, createdBy string) (OrgFolderView, error) {
	orgSlug = strings.TrimSpace(strings.ToLower(orgSlug))
	if orgSlug == "" {
		return OrgFolderView{}, ErrInvalid
	}
	store := s.ensureStore()
	if store == nil {
		return OrgFolderView{}, fmt.Errorf("store not configured")
	}
	if existing, err := store.GetOrgFolderBySlug(ctx, orgSlug); err == nil {
		return OrgFolderView{
			ID:              existing.ID,
			OrgSlug:         existing.OrgSlug,
			MountPoint:      existing.MountPoint,
			NCFolderID:      existing.NCFolderID,
			QuotaBytes:      existing.QuotaBytes,
			AutoProvisioned: existing.AutoProvisioned,
		}, nil
	} else if !errors.Is(err, drivestore.ErrOrgFolderNotFound) {
		return OrgFolderView{}, err
	}
	mountPoint := orgSlug
	if mountPoint == "" {
		mountPoint = "org"
	}
	return s.createOrgFolderInternal(ctx, orgSlug, mountPoint, nil, true, createdBy)
}

func (s *Service) createOrgFolderInternal(ctx context.Context, orgSlug, mountPoint string, quota *int64, auto bool, createdBy string) (OrgFolderView, error) {
	view, err := s.CreateOrgFolder(ctx, CreateOrgFolderParams{
		OrgSlug:         orgSlug,
		MountPoint:      mountPoint,
		QuotaBytes:      quota,
		AutoProvisioned: auto,
		CreatedBy:       createdBy,
	})
	if err != nil {
		return OrgFolderView{}, err
	}
	view.AutoProvisioned = auto
	return view, nil
}

func (s *Service) UpdateOrgFolder(ctx context.Context, id, mountPoint string, quotaBytes *int64) (OrgFolderView, error) {
	store := s.ensureStore()
	if store == nil {
		return OrgFolderView{}, fmt.Errorf("store not configured")
	}
	row, err := store.GetOrgFolder(ctx, id)
	if err != nil {
		return OrgFolderView{}, err
	}
	if mountPoint != "" && mountPoint != row.MountPoint {
		if err := s.nc.RenameGroupFolder(ctx, row.NCFolderID, mountPoint); err != nil {
			return OrgFolderView{}, mapDriveError(err)
		}
	}
	if quotaBytes != nil {
		if err := s.nc.SetGroupFolderQuota(ctx, row.NCFolderID, *quotaBytes); err != nil {
			return OrgFolderView{}, mapDriveError(err)
		}
	}
	updated, err := store.UpdateOrgFolder(ctx, id, mountPoint, quotaBytes)
	if err != nil {
		return OrgFolderView{}, err
	}
	return OrgFolderView{
		ID:         updated.ID,
		OrgSlug:    updated.OrgSlug,
		MountPoint: updated.MountPoint,
		NCFolderID: updated.NCFolderID,
		QuotaBytes: updated.QuotaBytes,
	}, nil
}

func (s *Service) DeleteOrgFolder(ctx context.Context, id string) error {
	store := s.ensureStore()
	if store == nil {
		return fmt.Errorf("store not configured")
	}
	row, err := store.GetOrgFolder(ctx, id)
	if err != nil {
		return err
	}
	if err := s.nc.DeleteGroupFolder(ctx, row.NCFolderID); err != nil {
		return mapDriveError(err)
	}
	return store.DeleteOrgFolder(ctx, id)
}

func (s *Service) SyncOrgFolders(ctx context.Context, orgSlugs []string, createdBy string) ([]OrgFolderView, error) {
	out := make([]OrgFolderView, 0, len(orgSlugs))
	for _, slug := range orgSlugs {
		slug = strings.TrimSpace(strings.ToLower(slug))
		if slug == "" {
			continue
		}
		view, err := s.ProvisionOrgFolder(ctx, slug, createdBy)
		if err != nil {
			return nil, err
		}
		out = append(out, view)
	}
	return out, nil
}
