package auth

import (
	"errors"
	"net/http"
	"net/url"
	"time"

	"strings"
	"secondbit.org/uuid"
)

const sessionCookie = "session"

var (
	ErrSessionNotFound = errors.New("Session not found.")
)

type Session struct {
	Token   string
	Profile uuid.ID
	Expires time.Time
	Created time.Time
	IP      string
}

type SessionStore interface {
	GetSession(token string) (Session, error)
	GetAllSessions(userID uuid.ID, num, page int) ([]Session, error)
	SaveSession(session Session) error
	DeleteSession(sessionID uuid.ID) error
}

func validateSession(r *http.Request, c Context) error {
	cookie, err := r.Cookie(sessionCookie)
	if err == http.ErrNoCookie {
		return ErrSessionNotFound
	}
	_, err = c.Sessions.GetSession(cookie.Value)
	return err
}

func HandleLoginRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
	if r.Method == "GET" {
		ctx.RenderLogin(w, r)
		return
	} else if r.Method != "POST" {
		// TODO: return bad method error
		return
	}

	if r.FormValue("username") == "" || r.FormValue("password") == "" {
		// TODO: return unauthenticated error
		return
	}
	id, err := ctx.Profiles.GetProfile(r.FormValue("username"), r.FormValue("password"))
	if err != nil {
		if err == ErrProfileNotFound {
			// TODO: return unauthenticated error
			return
		}
		// TODO: return internal server error
		return
	}
	session := Session{
		Token:   newToken(),
		Profile: id,
		Expires: time.Now().Add(ctx.Config.SessionLength),
		Created: time.Now(),
		IP:      r.Header.Get(ctx.Config.RequestIPHeader),
	}
	err = ctx.Sessions.SetSession(session)
	if err != nil {
		// TODO: return internal server error
		return
	}
	http.SetCookie(w, &http.Cookie{
		Name:     sessionCookie,
		Value:    session.Token,
		Expires:  session.Expires,
		Secure:   true,
		HttpOnly: true,
	})

	redirectString := r.URL.Query().Get("redirect_to")
	if redirectString != "" {
		redirectURI, err := url.Parse(redirectString)
		if err != nil {
			// TODO: render a bad request error
			return
		}
		if !strings.HasSuffix("."+ctx.Config.LoginRedirectDomain, redirectURI.Host) && redirectURI.Host != ctx.Config.LoginRedirectDomain {
			// TODO: render a bad request error
			return
		}
	} else {
		redirectString = "https://" + ctx.Config.LoginRedirectDomain
	}
	http.Redirect(w, r, redirectString, http.StatusFound)
	return
}
