package admin

import (
	"context"
	"sort"
	"strings"
	"time"

	"github.com/ultisuite/ulti-backend/internal/api/query"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
	"github.com/ultisuite/ulti-backend/internal/publicshare"
)

type PublicSharesList struct {
	Shares     []map[string]any     `json:"shares"`
	Pagination query.PaginationMeta `json:"pagination,omitempty"`
}

func (s *Service) ListPublicShares(ctx context.Context, params query.ListParams) (PublicSharesList, error) {
	if s.nc == nil {
		return PublicSharesList{
			Shares:     []map[string]any{},
			Pagination: params.Meta(ptrInt64(0)),
		}, nil
	}

	rows, err := s.db.Query(ctx, `
		SELECT email, external_id FROM users WHERE status != 'disabled'
	`)
	if err != nil {
		return PublicSharesList{}, err
	}
	defer rows.Close()

	type platformUser struct {
		email      string
		externalID string
	}
	users := make([]platformUser, 0)
	for rows.Next() {
		var u platformUser
		if err := rows.Scan(&u.email, &u.externalID); err != nil {
			return PublicSharesList{}, err
		}
		users = append(users, u)
	}
	if err := rows.Err(); err != nil {
		return PublicSharesList{}, err
	}

	q := strings.ToLower(strings.TrimSpace(params.Q))
	all := make([]map[string]any, 0)

	for _, u := range users {
		ncUser := nextcloud.UserIDFromClaims(u.email, u.externalID)
		if ncUser == "" {
			continue
		}
		shares, err := s.nc.ListShares(ctx, ncUser, "")
		if err != nil {
			s.logger.Debug("list shares for admin audit", "nc_user", ncUser, "error", err)
			continue
		}
		for _, share := range shares {
			if !nextcloud.IsExternalShare(share) {
				continue
			}
			if q != "" && !publicShareMatchesQuery(share, u.email, q) {
				continue
			}
			entry := map[string]any{
				"id":                      share.ID,
				"token":                   share.Token,
				"path":                    share.Path,
				"item_type":               share.ItemType,
				"access_mode":             share.AccessMode,
				"share_type":              share.ShareType,
				"permissions":             share.Permissions,
				"url":                     share.URL,
				"expires_at":              share.ExpiresAt,
				"created_at":              share.CreatedAt,
				"owner_nc_user_id":        ncUser,
				"owner_email":             strings.ToLower(strings.TrimSpace(u.email)),
				"owner_display_name":      firstNonEmptyStr(share.OwnerDisplayName, share.FileOwnerDisplayName),
				"share_with":              share.ShareWith,
				"share_with_display_name": share.ShareWithDisplayName,
				"has_password":            share.HasPassword,
				"label":                   share.Label,
			}
			all = append(all, entry)
		}
	}

	sort.Slice(all, func(i, j int) bool {
		return shareCreatedAt(all[i]).After(shareCreatedAt(all[j]))
	})

	tokens := make([]string, 0, len(all))
	for _, item := range all {
		if t, _ := item["token"].(string); t != "" {
			tokens = append(tokens, t)
		}
	}
	access, err := publicshare.Lookup(ctx, s.db, tokens)
	if err != nil {
		return PublicSharesList{}, err
	}
	for _, item := range all {
		token, _ := item["token"].(string)
		if stats, ok := access[token]; ok {
			item["last_access_at"] = stats.LastAccessAt.UTC().Format(time.RFC3339)
			item["access_count"] = stats.AccessCount
		} else {
			item["last_access_at"] = nil
			item["access_count"] = int64(0)
		}
	}

	total := int64(len(all))
	start := params.Offset()
	if start > len(all) {
		start = len(all)
	}
	end := start + params.Limit()
	if end > len(all) {
		end = len(all)
	}
	page := all[start:end]

	return PublicSharesList{
		Shares:     page,
		Pagination: params.Meta(&total),
	}, nil
}

func (s *Service) RevokePublicShare(ctx context.Context, actorSub, shareID, ownerNCUserID string) error {
	if s.nc == nil {
		return ErrNotFound
	}
	shareID = strings.TrimSpace(shareID)
	ownerNCUserID = strings.TrimSpace(ownerNCUserID)
	if shareID == "" || ownerNCUserID == "" {
		return ErrNotFound
	}
	if err := s.nc.DeleteShare(ctx, ownerNCUserID, shareID); err != nil {
		return err
	}
	s.logAudit(ctx, actorSub, "revoke_public_share", map[string]any{
		"share_id":         shareID,
		"owner_nc_user_id": ownerNCUserID,
	})
	return nil
}

func publicShareMatchesQuery(share nextcloud.ShareInfo, ownerEmail, q string) bool {
	haystack := strings.ToLower(strings.Join([]string{
		share.Path,
		share.Token,
		share.URL,
		share.ShareWith,
		share.ShareWithDisplayName,
		share.OwnerDisplayName,
		share.FileOwnerDisplayName,
		ownerEmail,
		share.Label,
		share.Note,
	}, " "))
	return strings.Contains(haystack, q)
}

func shareCreatedAt(item map[string]any) time.Time {
	raw, _ := item["created_at"].(string)
	if raw == "" {
		return time.Time{}
	}
	t, err := time.Parse(time.RFC3339, raw)
	if err != nil {
		return time.Time{}
	}
	return t
}

func firstNonEmptyStr(values ...string) string {
	for _, v := range values {
		if strings.TrimSpace(v) != "" {
			return strings.TrimSpace(v)
		}
	}
	return ""
}

func ptrInt64(v int64) *int64 {
	return &v
}
