//
//  tank
//
//  Import and Playlist Daemon for autoradio project
//
//
//  Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at>
//
//  This file is part of tank.
//
//  tank is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  any later version.
//
//  tank is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with tank. If not, see <http://www.gnu.org/licenses/>.
//

package auth

import (
	"crypto/sha256"
	"errors"
	"math"
	"time"

	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/hkdf"
)

const (
	hkdfInfo   = "aura-tank-session-keys"
	defaultAge = 24 * time.Hour
)

type Session struct {
	sealer *securecookie.SecureCookie

	Expires   time.Time
	Username  string
	AllGroups bool
	Groups    []string
}

func (s *Session) Seal() ([]byte, error) {
	return nil, errors.New("not yet implemented")
}

func (s *Session) Unseal([]byte) error {
	return errors.New("not yet implemented")
}

func (s *Session) Expired() bool {
	return s.Expires.Before(time.Now())
}

type SessionManager struct {
	sealer *securecookie.SecureCookie
	maxAge time.Duration
}

func NewSessionManager(cfg SessionsConfig) (sm *SessionManager, err error) {
	var hashKey, blockKey []byte
	if cfg.Secret == "" {
		// TODO: make key size dependent on algo
		hashKey = securecookie.GenerateRandomKey(32)
		blockKey = securecookie.GenerateRandomKey(32)
		if hashKey == nil || blockKey == nil {
			return nil, errors.New("failed to generate random key")
		}
	} else {
		kdf := hkdf.New(sha256.New, []byte(cfg.Secret), nil, []byte(hkdfInfo))

		// TODO: make key size dependent on algo
		hashKey = make([]byte, 32)
		if _, err = kdf.Read(hashKey); err != nil {
			return nil, errors.New("failed to derive session auth key: " + err.Error())
		}

		// TODO: make key size dependent on algo
		blockKey = make([]byte, 32)
		if _, err = kdf.Read(blockKey); err != nil {
			return nil, errors.New("failed to derive session auth key: " + err.Error())
		}
	}

	sm = &SessionManager{maxAge: defaultAge}
	if cfg.MaxAge > 0 {
		sm.maxAge = cfg.MaxAge
	}
	sm.sealer = securecookie.New(hashKey, blockKey)
	sm.sealer.MaxAge(int(math.Ceil(cfg.MaxAge.Seconds())))
	return sm, nil
}

func (sm *SessionManager) NewSession() *Session {
	return &Session{sealer: sm.sealer, Expires: time.Now().Add(sm.maxAge)}
}