package status

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/FAU-CDI/wisski-distillery/internal/phpx"
	"golang.org/x/exp/slices"
)

// DrupalUser represents a WissKI DrupalUser
type DrupalUser struct {
	UID     phpx.Integer   `json:"uid,omitempty"`
	Name    phpx.String    `json:"name,omitempty"`
	Mail    phpx.String    `json:"mail,omitempty"`
	Status  phpx.Boolean   `json:"status,omitempty"`
	Created phpx.Timestamp `json:"created,omitempty"`
	Changed phpx.Timestamp `json:"changed,omitempty"`
	Access  phpx.Timestamp `json:"access,omitempty"`
	Login   phpx.Timestamp `json:"login,omitempty"`
	Roles   UserRoles      `json:"roles,omitempty"`
}

func (du DrupalUser) String() string {
	var builder strings.Builder

	builder.WriteString("DrupalUser{")
	defer builder.WriteString("}")

	fmt.Fprintf(&builder, "UID: %d, ", du.UID)
	fmt.Fprintf(&builder, "Name: %q, ", du.Name)

	if du.Mail != "" {
		fmt.Fprintf(&builder, "Mail: %q, ", du.Mail)
	}

	fmt.Fprintf(&builder, "Status: %t, ", du.Status)

	for _, tn := range []struct {
		Name string
		Time time.Time
	}{
		{"Created", du.Created.Time()},
		{"Changed", du.Changed.Time()},
		{"Access", du.Access.Time()},
		{"Login", du.Login.Time()},
	} {
		if tn.Time.IsZero() {
			continue
		}
		fmt.Fprintf(&builder, "%s: %q, ", tn.Name, tn.Time.Format(time.Stamp))
	}

	fmt.Fprintf(&builder, "Roles: %s", du.Roles)

	return builder.String()
}

// UserRole represents the role of a user
type UserRole string

const (
	Administrator UserRole = "administrator"
	ContentEditor UserRole = "content_editor"
)

// UserRoles represents a set of user roles for a given user
type UserRoles map[UserRole]struct{}

func (ur UserRoles) String() string {
	return "[" + ur.string() + "]"
}

// String turns this UserRoles into a string
func (ur UserRoles) string() string {
	roles := make([]string, len(ur))
	i := 0
	for r := range ur {
		roles[i] = string(r)
		i++
	}
	slices.Sort(roles) // for consistent marshaling
	return strings.Join(roles, ", ")
}

func (ur UserRoles) MarshalJSON() ([]byte, error) {
	return json.Marshal(ur.string())
}

// Has checks if the UserRole has the given role
func (ur UserRoles) Has(role UserRole) (ok bool) {
	_, ok = ur[role]
	return
}

func (u *UserRoles) UnmarshalJSON(data []byte) error {
	return phpx.UnmarshalIntermediate(u, func(s phpx.String) (UserRoles, error) {
		if len(s) == 0 {
			return nil, nil
		}
		roles := strings.Split(string(s), ", ")
		uroles := make(UserRoles, len(roles))
		for _, r := range roles {
			uroles[UserRole(r)] = struct{}{}
		}
		return uroles, nil
	}, data)
}
