package main

import (
	"context"
	"database/sql"
	"net/http"
	"strings"
)

type contextKey string

const userContextKey contextKey = "auth_user"

type User struct {
	Sub   string
	Email string
	Name  string
}

type Auth struct {
	devMode bool
	db      *sql.DB
}

func NewAuth(cfg Config) (*Auth, error) {
	return &Auth{devMode: cfg.DevMode}, nil
}

func (a *Auth) WithDB(db *sql.DB) {
	a.db = db
}

func (a *Auth) RequireAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		user, ok := a.userFromRequest(r)
		if !ok {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		if a.db != nil {
			_, _ = a.db.ExecContext(r.Context(), `
				INSERT INTO users (sub, email, name)
				VALUES ($1, $2, $3)
				ON CONFLICT (sub) DO UPDATE
				SET email = EXCLUDED.email, name = EXCLUDED.name
			`, user.Sub, user.Email, user.Name)
		}

		ctx := context.WithValue(r.Context(), userContextKey, user)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func (a *Auth) userFromRequest(r *http.Request) (User, bool) {
	if a.devMode {
		return User{Sub: "demo-user-001", Email: "demo@example.com", Name: "Demo User"}, true
	}

	sub := strings.TrimSpace(r.Header.Get("X-Authentik-Uid"))
	if sub == "" {
		sub = strings.TrimSpace(r.Header.Get("X-Forwarded-User"))
	}
	email := strings.TrimSpace(r.Header.Get("X-Authentik-Email"))
	name := strings.TrimSpace(r.Header.Get("X-Authentik-Name"))
	if name == "" {
		name = strings.TrimSpace(r.Header.Get("X-Forwarded-Preferred-Username"))
	}

	if sub == "" {
		return User{}, false
	}
	if email == "" {
		email = sub
	}
	if name == "" {
		name = email
	}
	return User{Sub: sub, Email: email, Name: name}, true
}

func MustUserFromContext(ctx context.Context) User {
	v := ctx.Value(userContextKey)
	if v == nil {
		return User{}
	}
	if u, ok := v.(User); ok {
		return u
	}
	return User{}
}
