package platform

import (
	"context"
	"strings"
	"time"
)

type VehicleGrant struct {
	VIN       string     `json:"-"`
	ValidFrom time.Time  `json:"-"`
	ValidTo   *time.Time `json:"-"`
}

type Principal struct {
	SubjectID     string         `json:"subjectId,omitempty"`
	SessionID     string         `json:"-"`
	Name          string         `json:"name"`
	Username      string         `json:"username,omitempty"`
	Role          string         `json:"role"`
	UserType      string         `json:"userType"`
	CustomerRef   string         `json:"customerRef,omitempty"`
	TenantRef     string         `json:"tenantRef,omitempty"`
	AuthProvider  string         `json:"authProvider"`
	MenuKeys      []string       `json:"menuKeys"`
	VehicleVINs   []string       `json:"-"`
	VehicleGrants []VehicleGrant `json:"-"`
	VehicleCount  int            `json:"vehicleCount"`
}

func (p Principal) Clone() Principal {
	p.MenuKeys = append([]string(nil), p.MenuKeys...)
	p.VehicleVINs = append([]string(nil), p.VehicleVINs...)
	p.VehicleGrants = append([]VehicleGrant(nil), p.VehicleGrants...)
	p.VehicleCount = len(p.VehicleVINs)
	return p
}

func (p Principal) CanMenu(key string) bool {
	if p.UserType == "admin" || p.Role == "admin" {
		return true
	}
	for _, value := range p.MenuKeys {
		if value == key {
			return true
		}
	}
	return false
}

func (p Principal) CanVIN(vin string) bool {
	if p.UserType != "customer" {
		return true
	}
	for _, allowed := range p.VehicleVINs {
		if allowed == vin {
			return true
		}
	}
	return false
}

func (p Principal) VehicleGrant(vin string) (VehicleGrant, bool) {
	vin = strings.ToUpper(strings.TrimSpace(vin))
	for _, grant := range p.VehicleGrants {
		if strings.ToUpper(strings.TrimSpace(grant.VIN)) == vin {
			return grant, true
		}
	}
	return VehicleGrant{}, false
}

type principalContextKey struct{}

func WithPrincipal(ctx context.Context, principal Principal) context.Context {
	return context.WithValue(ctx, principalContextKey{}, principal)
}

func PrincipalFromContext(ctx context.Context) (Principal, bool) {
	principal, ok := ctx.Value(principalContextKey{}).(Principal)
	return principal, ok
}

func ActorFromContext(ctx context.Context) string {
	if principal, ok := PrincipalFromContext(ctx); ok && principal.Name != "" {
		return principal.Name
	}
	return "system"
}
