package client

import (
	
	
	
	
	

	
	
)

// Cache for service tickets held by the client.
type Cache struct {
	Entries map[string]CacheEntry
	mux     sync.RWMutex
}

// CacheEntry holds details for a cache entry.
type CacheEntry struct {
	SPN        string
	Ticket     messages.Ticket `json:"-"`
	AuthTime   time.Time
	StartTime  time.Time
	EndTime    time.Time
	RenewTill  time.Time
	SessionKey types.EncryptionKey `json:"-"`
}

// NewCache creates a new client ticket cache instance.
func () *Cache {
	return &Cache{
		Entries: map[string]CacheEntry{},
	}
}

// getEntry returns a cache entry that matches the SPN.
func ( *Cache) ( string) (CacheEntry, bool) {
	.mux.RLock()
	defer .mux.RUnlock()
	,  := (*).Entries[]
	return , 
}

// JSON returns information about the cached service tickets in a JSON format.
func ( *Cache) () (string, error) {
	.mux.RLock()
	defer .mux.RUnlock()
	var  []CacheEntry
	 := make([]string, 0, len(.Entries))
	for  := range .Entries {
		 = append(, )
	}
	sort.Strings()
	for ,  := range  {
		 = append(, .Entries[])
	}
	,  := json.MarshalIndent(&, "", "  ")
	if  != nil {
		return "", 
	}
	return string(), nil
}

// addEntry adds a ticket to the cache.
func ( *Cache) ( messages.Ticket, , , ,  time.Time,  types.EncryptionKey) CacheEntry {
	 := .SName.PrincipalNameString()
	.mux.Lock()
	defer .mux.Unlock()
	(*).Entries[] = CacheEntry{
		SPN:        ,
		Ticket:     ,
		AuthTime:   ,
		StartTime:  ,
		EndTime:    ,
		RenewTill:  ,
		SessionKey: ,
	}
	return .Entries[]
}

// clear deletes all the cache entries
func ( *Cache) () {
	.mux.Lock()
	defer .mux.Unlock()
	for  := range .Entries {
		delete(.Entries, )
	}
}

// RemoveEntry removes the cache entry for the defined SPN.
func ( *Cache) ( string) {
	.mux.Lock()
	defer .mux.Unlock()
	delete(.Entries, )
}

// GetCachedTicket returns a ticket from the cache for the SPN.
// Only a ticket that is currently valid will be returned.
func ( *Client) ( string) (messages.Ticket, types.EncryptionKey, bool) {
	if ,  := .cache.getEntry();  {
		//If within time window of ticket return it
		if time.Now().UTC().After(.StartTime) && time.Now().UTC().Before(.EndTime) {
			.Log("ticket received from cache for %s", )
			return .Ticket, .SessionKey, true
		} else if time.Now().UTC().Before(.RenewTill) {
			,  := .renewTicket()
			if  != nil {
				return .Ticket, .SessionKey, false
			}
			return .Ticket, .SessionKey, true
		}
	}
	var  messages.Ticket
	var  types.EncryptionKey
	return , , false
}

// renewTicket renews a cache entry ticket.
// To renew from outside the client package use GetCachedTicket
func ( *Client) ( CacheEntry) (CacheEntry, error) {
	 := .Ticket.SName
	, ,  := .TGSREQGenerateAndExchange(, .Ticket.Realm, .Ticket, .SessionKey, true)
	if  != nil {
		return , 
	}
	,  := .cache.getEntry(.Ticket.SName.PrincipalNameString())
	if ! {
		return , errors.New("ticket was not added to cache")
	}
	.Log("ticket renewed for %s (EndTime: %v)", .PrincipalNameString(), .EndTime)
	return , nil
}