#[Copyright 2025 ITwrx.
This file is part of Simple Site Manager.
Simple Site Manager is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#

import std/[strutils, cgi, strtabs, cookies, sysrand, base64]
import guildenstern/[httpserver], sqliteral
import "../models/session", "db", "global"

#[type 
  Session* = object
    sessionId*, csrfToken*: string
    id*, userId*: int]#
type
  User* = object
    email*, password*: string
    id*: int

proc getSessionBySessionId*(sessionId: string): Session =
  {.gcsafe.}:
    var session: Session
    for row in db1.rows(SelectSessionBySessionId, sessionId): 
      session.id = row.getInt(0)
      session.sessionId = row.getString(1)
      session.userId = row.getInt(2)
      session.csrfToken = row.getString(3)
    return session

proc createUserSession*(userSession: Session) =
  {.gcsafe.}:  
    db1.transaction:
      discard db1.insert(InsertUserSession, userSession.sessionId, userSession.userId, userSession.csrfToken)

proc deleteSession*(sessionId: string) =
  {.gcsafe.}:  
    db1.transaction:
      db1.exec(DeleteSessionBySessionId, sessionId)

#Both form and auth have this template. The form.nim copy is exported. 
template formInput(input: string): untyped =
  readData(getBody()).getOrDefault(input)

proc getSessionIdFromCookies*(): string =
  {.gcsafe.}:
    let cookieString = http.headers.getOrDefault("cookie")
    let allCookies = parseCookies(cookieString)
    if allCookies.hasKey(APP_NAME & "_session"):
      return allCookies[APP_NAME & "_session"]
    else:
      return ""
  
proc setVisitorCsrfToken*(): string =
  {.gcsafe.}:
    #create csrf token.
    var csrfToken = $urandom(32)
    csrfToken = base64.encode(csrfToken)
    #delete old VisitorSessions, as they are just the one time use CSRF Tokens.
    #may need to be redesigned for better multiuser robustness if it's deleting other users' unused tokens.
    #deleteVisitorSessions()
    #create session in db.
    var visitorSession: Session
    visitorSession.csrfToken = csrfToken
    discard createVisitorSession(visitorSession)
    return csrfToken
  
proc getUserPageCsrfToken*(): string =
  {.gcsafe.}:
    var sessionId: string
    var userSession: Session
    sessionId = getSessionIdFromCookies()
    userSession = getSessionBySessionId(sessionId)
    return userSession.csrfToken
  
proc newCsrfToken*(): string =
  {.gcsafe.}:
    #create csrf token.
    var csrfToken = $urandom(32)
    csrfToken = base64.encode(csrfToken)
    return csrfToken

#[proc fCsrfToken*(): string =
  {.gcsafe.}:
    let sessionId = getSessionIdFromCookies()
    let userSession = getUserSessionBySessionId(sessionId)
    result = userSession.csrfToken]#
    
proc isValidVisitorCsrfToken*(csrfToken: string): bool =
  #our previoulsy self-generated, valid csrfToken from the DB.
  let visitorSessionCsrfToken = getSessionByCsrfToken(csrfToken).csrfToken
  if visitorSessionCsrfToken.len > 0 and csrfToken == visitorSessionCsrfToken:
    return true
  else:
    return false
  
proc isValidUserCsrfToken*(csrfToken: string): bool =
  var sessionId: string 
  var userSession: Session
  sessionId = getSessionIdFromCookies() 
  userSession = getSessionBySessionId(sessionId)
  if userSession.csrfToken == csrfToken:
    return true
  else:
    return false
    
proc isAuthdAdmin*(): bool = 
  {.gcsafe.}: 
    #get sessionId from request's cookie and see if it exists in session DB.
    var sessionId: string 
    sessionId = getSessionIdFromCookies()   
    if sessionId.len() > 0:
      var userSession: Session
      try:
        userSession = getSessionBySessionId(sessionId)
        if userSession.id > 0:
          return true
        else: 
          return false
      except Exception as e:
        echo e.msg
        return false       
    else:
      return false

#adds CSRF checking for POST requests.
proc isAuthdAdminPost*(): bool =  
  {.gcsafe.}:
    #get sessionId from request's cookie and see if it exists in session DB.
    #var sessionId{.threadvar.}: string 
    var sessionId: string
    sessionId = getSessionIdFromCookies()
    if sessionId.len() > 0:
      try:
        let userSession = getSessionBySessionId(sessionId)
        if userSession.id > 0:
          return true
        else:
          return false
      except Exception as e:
        echo e.msg
        return false       
    else:
      return false

template ifAuthAdminPost*(procName: untyped): untyped =
  if isAuthdAdminPost() == true:
    procName
  else:
    reply(Http302, [location("/login")])
